<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/blogwebpage/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/blogwebpage/images/favicon.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/blogwebpage/images/favicon.ico">
  <link rel="mask-icon" href="/blogwebpage/images/logo.svg" color="#222">
  <meta name="msvalidate.01" content="9BDlx6FiNE">

<link rel="stylesheet" href="/blogwebpage/css/main.css">


<link rel="stylesheet" href="/blogwebpage/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"wangkejie.gitee.io","root":"/blogwebpage/","scheme":"Gemini","version":"7.8.0","exturl":true,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"default"},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="书山有路勤为径，学海无涯苦作舟！">
<meta property="og:type" content="website">
<meta property="og:title" content="王科杰">
<meta property="og:url" content="https://wangkejie.gitee.io/blogwebpage/page/4/index.html">
<meta property="og:site_name" content="王科杰">
<meta property="og:description" content="书山有路勤为径，学海无涯苦作舟！">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="Jack Wang">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://wangkejie.gitee.io/blogwebpage/page/4/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh-CN'
  };
</script>

  <title>王科杰 - Jack's Notes</title>
  


  <script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?93e26277de0cf2c5851167f2c6dcaa7d";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/blogwebpage/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">王科杰</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">Jack's Notes</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/blogwebpage/" rel="section"><i class="home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/blogwebpage/archives/" rel="section"><i class="archive fa-fw"></i>归档<span class="badge">52</span></a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/blogwebpage/me/" rel="section"><i class="user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/blogwebpage/tags/" rel="section"><i class="tags fa-fw"></i>标签<span class="badge">54</span></a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/blogwebpage/categories/" rel="section"><i class="th fa-fw"></i>分类<span class="badge">56</span></a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

  <span class="exturl github-corner" data-url="aHR0cHM6Ly9naXRodWIuY29tL0phY2stVmluZw==" title="Follow me on GitHub" aria-label="Follow me on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></span>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content index posts-expand">
            
      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/thirdpart/afn.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/thirdpart/afn.html" class="post-title-link" itemprop="url">AFNetWorking</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-09-10 11:26:39" itemprop="dateCreated datePublished" datetime="2017-09-10T11:26:39+08:00">2017-09-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/Third-Party-Lib/" itemprop="url" rel="index"><span itemprop="name">Third-Party Lib</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/Third-Party-Lib/AFNetWorking/" itemprop="url" rel="index"><span itemprop="name">AFNetWorking</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="AFNetWorking"><a href="#AFNetWorking" class="headerlink" title="AFNetWorking"></a>AFNetWorking</h1><p>由于AFNetWorking在 NSURLSession初始化的时候，用到了一个代理方法。Session在ARC下不会及时的释放</p>
<p>在使用instruments做内存泄漏分析时，发现所有使用如下语句的地方都有内存泄漏，:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[AFHTTPSessionManager manager];</span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nullable</span>, <span class="keyword">readonly</span>, <span class="keyword">retain</span>) <span class="keyword">id</span> &lt;<span class="built_in">NSURLSessionDelegate</span>&gt; delegate;</span><br></pre></td></tr></table></figure>

<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Customization of NSURLSession occurs during creation of a new session.</span></span><br><span class="line"><span class="comment"> * If you only need to use the convenience routines with custom</span></span><br><span class="line"><span class="comment"> * configuration options it is not necessary to specify a delegate.</span></span><br><span class="line"><span class="comment"> * If you do specify a delegate, the delegate will be retained until after</span></span><br><span class="line"><span class="comment"> * the delegate has been sent the URLSession:didBecomeInvalidWithError: message.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*翻译：</span></span><br><span class="line"><span class="comment">NSURLSession的自定义发生在创建新会话期间。如果只需要使用带有自定义配置选项的便利例程，则不需要指定委托。</span></span><br><span class="line"><span class="comment">如果您指定了一个委托，那么该委托将被保留(retain)，直到URLSession:didBecomeInvalidWithError: message发送给委托之后。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line">+ (<span class="built_in">NSURLSession</span> *)sessionWithConfiguration:(<span class="built_in">NSURLSessionConfiguration</span> *)configuration delegate:(<span class="keyword">nullable</span> <span class="keyword">id</span> &lt;<span class="built_in">NSURLSessionDelegate</span>&gt;)delegate delegateQueue:(<span class="keyword">nullable</span> <span class="built_in">NSOperationQueue</span> *)queue;</span><br></pre></td></tr></table></figure>


<p>解决方法：<br>一般我们通过写单例的方式</p>
<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> AFHTTPSessionManager *manager ;`</span><br><span class="line">-(AFHTTPSessionManager *)sharedHTTPSession&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="built_in">dispatch_once_t</span> onceToken;</span><br><span class="line">    <span class="built_in">dispatch_once</span>(&amp;onceToken, ^&#123;</span><br><span class="line">        manager = [AFHTTPSessionManager manager];</span><br><span class="line">        manager.requestSerializer.timeoutInterval = <span class="number">10</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> manager;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这样的话，就不会创造出更多的session，避免了泄露</p>
<h1 id="暂停"><a href="#暂停" class="headerlink" title="暂停"></a>暂停</h1>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/thirdpart/asdk.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/thirdpart/asdk.html" class="post-title-link" itemprop="url">AsyncDisplayKit</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-09-10 11:26:39" itemprop="dateCreated datePublished" datetime="2017-09-10T11:26:39+08:00">2017-09-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/Third-Party-Lib/" itemprop="url" rel="index"><span itemprop="name">Third-Party Lib</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/Third-Party-Lib/AsyncDisplayKit/" itemprop="url" rel="index"><span itemprop="name">AsyncDisplayKit</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="AsyncDisplayKit"><a href="#AsyncDisplayKit" class="headerlink" title="AsyncDisplayKit"></a>AsyncDisplayKit</h1><p>AsyncDisplayKit 是 Facebook 开源的一个用于保持 iOS 界面流畅的库。在我博客其它章节也提到过。<br>ASDK 的作者是 Scott Goodson (<span class="exturl" data-url="aHR0cHM6Ly93d3cubGlua2VkaW4uY29tL2luL2lvc2VuZ2luZWVy">Linkedin<i class="fa fa-external-link-alt"></i></span>)，<br>他曾经在苹果工作，负责 iOS 的一些内置应用的开发，比如股票、计算器、地图、钟表、设置、Safari 等，当然他也参与了 UIKit framework 的开发。后来他加入 Facebook 后，负责 Paper 的开发，创建并开源了 AsyncDisplayKit。目前他在 Pinterest 和 Instagram 负责 iOS 开发和用户体验的提升等工作。</p>
<p>ASDK 自 2014 年 6 月开源，10 月发布 1.0 版。目前 ASDK 即将要发布 2.0 版。<br>V2.0 增加了更多布局相关的代码，ComponentKit 团队为此贡献很多。<br>现在 Github 的 master 分支上的版本是 V1.9.1，已经包含了 V2.0 的全部内容。</p>
<p><span class="exturl" data-url="aHR0cDovL3RleHR1cmVncm91cC5vcmcvZG9jcy9nZXR0aW5nLXN0YXJ0ZWQuaHRtbA==">官方教程<i class="fa fa-external-link-alt"></i></span></p>
<h2 id="ASDK的基本原理"><a href="#ASDK的基本原理" class="headerlink" title="ASDK的基本原理"></a>ASDK的基本原理</h2><img src="/blogwebpage/images/iOS/asdk_design.png" class="" title="asdk_design">


<p>ASDK 认为，阻塞主线程的任务，主要分为上面这三大类。文本和布局的计算、渲染、解码、绘制都可以通过各种方式异步执行，但 UIKit 和 Core Animation 相关操作必需在主线程进行。ASDK 的目标，就是尽量把这些任务从主线程挪走，而挪不走的，就尽量优化性能。</p>
<p>为了达成这一目标，ASDK 尝试对 UIKit 组件进行封装：</p>
<img src="/blogwebpage/images/iOS/asdk_layer_backed_view.png" class="" title="asdk_design">

<p>这是常见的 UIView 和 CALayer 的关系：View 持有 Layer 用于显示，View 中大部分显示属性实际是从 Layer 映射而来；Layer 的 delegate 在这里是 View，当其属性改变、动画产生时，View 能够得到通知。UIView 和 CALayer 不是线程安全的，并且只能在主线程创建、访问和销毁。</p>
<img src="/blogwebpage/images/iOS/asdk_view_backed_node.png" class="" title="asdk_design">

<p>ASDK 为此创建了 ASDisplayNode 类，包装了常见的视图属性（比如 frame/bounds/alpha/transform/backgroundColor/superNode/subNodes 等），然后它用 UIView-&gt;CALayer 相同的方式，实现了 ASNode-&gt;UIView 这样一个关系。  </p>
<img src="/blogwebpage/images/iOS/asdk_layer_backed_node.png" class="" title="asdk_design">


<p>当不需要响应触摸事件时，ASDisplayNode 可以被设置为 layer backed，即 ASDisplayNode 充当了原来 UIView 的功能，节省了更多资源。</p>
<p>与 UIView 和 CALayer 不同，ASDisplayNode 是线程安全的，它可以在后台线程创建和修改。Node 刚创建时，并不会在内部新建 UIView 和 CALayer，直到第一次在主线程访问 view 或 layer 属性时，它才会在内部生成对应的对象。当它的属性（比如frame/transform）改变后，它并不会立刻同步到其持有的 view 或 layer 去，而是把被改变的属性保存到内部的一个中间变量，稍后在需要时，再通过某个机制一次性设置到内部的 view 或 layer。</p>
<p>通过模拟和封装 UIView/CALayer，开发者可以把代码中的 UIView 替换为 ASNode，很大的降低了开发和学习成本，同时能获得 ASDK 底层大量的性能优化。为了方便使用， ASDK 把大量常用控件都封装成了 ASNode 的子类，比如 Button、Control、Cell、Image、ImageView、Text、TableView、CollectionView 等。利用这些控件，开发者可以尽量避免直接使用 UIKit 相关控件，以获得更完整的性能提升。</p>
<h1 id="ASDK-的图层预合成"><a href="#ASDK-的图层预合成" class="headerlink" title="ASDK 的图层预合成"></a>ASDK 的图层预合成</h1><table>
    <tr>
        <td> <img src="/blogwebpage/images/iOS/asdk_comoose_1.png" class="" title="asdk_design"> </td>
        <td> <img src="/blogwebpage/images/iOS/asdk_compose_2.png" class="" title="asdk_design"> </td>
    </tr>
</table>

<p>有时一个 layer 会包含很多 sub-layer，而这些 sub-layer 并不需要响应触摸事件，也不需要进行动画和位置调整。ASDK 为此实现了一个被称为 pre-composing 的技术，可以把这些 sub-layer 合成渲染为一张图片。开发时，ASNode 已经替代了 UIView 和 CALayer；直接使用各种 Node 控件并设置为 layer backed 后，ASNode 甚至可以通过预合成来避免创建内部的 UIView 和 CALayer。</p>
<p>通过这种方式，把一个大的层级，通过一个大的绘制方法绘制到一张图上，性能会获得很大提升。CPU 避免了创建 UIKit 对象的资源消耗，GPU 避免了多张 texture 合成和渲染的消耗，更少的 bitmap 也意味着更少的内存占用。</p>
<h1 id="ASDK-异步并发操作"><a href="#ASDK-异步并发操作" class="headerlink" title="ASDK 异步并发操作"></a>ASDK 异步并发操作</h1><img src="/blogwebpage/images/iOS/asdk_a9_chip.jpg" class="" title="asdk_a9_chip">

<p>自 iPhone 4S 起，iDevice 已经都是双核 CPU 了，现在的 iPad 甚至已经更新到 3 核了。充分利用多核的优势、并发执行任务对保持界面流畅有很大作用。ASDK 把布局计算、文本排版、图片/文本/图形渲染等操作都封装成较小的任务，并利用 GCD 异步并发执行。如果开发者使用了 ASNode 相关的控件，那么这些并发操作会自动在后台进行，无需进行过多配置。</p>
<h1 id="Runloop-任务分发"><a href="#Runloop-任务分发" class="headerlink" title="Runloop 任务分发"></a>Runloop 任务分发</h1><p>Runloop work distribution 是 ASDK 比较核心的一个技术，ASDK 的介绍视频和文档中都没有详细展开介绍，所以这里我会多做一些分析。如果你对 Runloop 还不太了解，可以看一下我之前的文章 <a href="./iOS/RunLoop/index.html">RunLoop</a>，里面对 ASDK 也有所提及。</p>
<img src="/blogwebpage/images/iOS/ios_vsync_runloop.png" class="" title="ios_vsync_runloop">


<p>iOS 的显示系统是由 VSync 信号驱动的，VSync 信号由硬件时钟生成，每秒钟发出 60 次（这个值取决设备硬件，比如 iPhone 真机上通常是 59.97）。iOS 图形服务接收到 VSync 信号后，会通过 IPC 通知到 App 内。App 的 Runloop 在启动后会注册对应的 CFRunLoopSource 通过 mach_port 接收传过来的时钟信号通知，随后 Source 的回调会驱动整个 App 的动画与显示。</p>
<p>Core Animation 在 RunLoop 中注册了一个 Observer，监听了 BeforeWaiting 和 Exit 事件。这个 Observer 的优先级是 2000000，低于常见的其他 Observer。当一个触摸事件到来时，RunLoop 被唤醒，App 中的代码会执行一些操作，比如创建和调整视图层级、设置 UIView 的 frame、修改 CALayer 的透明度、为视图添加一个动画；这些操作最终都会被 CALayer 捕获，并通过 CATransaction 提交到一个中间状态去（CATransaction 的文档略有提到这些内容，但并不完整）。当上面所有操作结束后，RunLoop 即将进入休眠（或者退出）时，关注该事件的 Observer 都会得到通知。这时 CA 注册的那个 Observer 就会在回调中，把所有的中间状态合并提交到 GPU 去显示；如果此处有动画，CA 会通过 DisplayLink 等机制多次触发相关流程。</p>
<p>ASDK 在此处模拟了 Core Animation 的这个机制：所有针对 ASNode 的修改和提交，总有些任务是必需放入主线程执行的。当出现这种任务时，ASNode 会把任务用 ASAsyncTransaction(Group) 封装并提交到一个全局的容器去。ASDK 也在 RunLoop 中注册了一个 Observer，监视的事件和 CA 一样，但优先级比 CA 要低。当 RunLoop 进入休眠前、CA 处理完事件后，ASDK 就会执行该 loop 内提交的所有任务。具体代码见这个文件：<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rL0FzeW5jRGlzcGxheUtpdC9ibG9iL21hc3Rlci9Bc3luY0Rpc3BsYXlLaXQlMkZEZXRhaWxzJTJGVHJhbnNhY3Rpb25zJTJGX0FTQXN5bmNUcmFuc2FjdGlvbkdyb3VwLm0=">ASAsyncTransactionGroup<i class="fa fa-external-link-alt"></i></span>。</p>
<p>通过这种机制，ASDK 可以在合适的机会把异步、并发的操作同步到主线程去，并且能获得不错的性能。</p>
<h1 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h1><p>ASDK 中还有封装很多高级的功能，比如滑动列表的预加载、V2.0添加的新的布局模式等。ASDK 是一个很庞大的库，它本身并不推荐你把整个 App 全部都改为 ASDK 驱动，把最需要提升交互性能的地方用 ASDK 进行优化就足够了。</p>
<p><span class="exturl" data-url="aHR0cHM6Ly9ibG9nLmliaXJlbWUuY29tLzIwMTUvMTEvMTIvc21vb3RoX3VzZXJfaW50ZXJmYWNlc19mb3JfaW9zLw==">不够尽兴的朋友请移步博客<i class="fa fa-external-link-alt"></i></span></p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/thirdpart/sdwebimage.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/thirdpart/sdwebimage.html" class="post-title-link" itemprop="url">SDWebImage</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-09-10 00:00:00" itemprop="dateCreated datePublished" datetime="2017-09-10T00:00:00+08:00">2017-09-10</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/Third-Party-Lib/" itemprop="url" rel="index"><span itemprop="name">Third-Party Lib</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/Third-Party-Lib/SDWebImage/" itemprop="url" rel="index"><span itemprop="name">SDWebImage</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="SDWebImage介绍"><a href="#SDWebImage介绍" class="headerlink" title="SDWebImage介绍"></a>SDWebImage介绍</h1><ul>
<li><p>提供 UIImageView, UIButton, MKAnnotationView 的分类，用来加载网络图片，并进行缓存管理;</p>
</li>
<li><p>异步方式来下载网络图片</p>
</li>
<li><p>异步方式: memory (内存)＋ disk (磁盘) 来缓存网络图片，自动管理缓存;</p>
</li>
<li><p>后台图片解码,转换及压缩;</p>
<ul>
<li>空间换时间<span class="exturl" data-url="aHR0cHM6Ly93d3cuY29jb2FuZXRpY3MuY29tLzIwMTEvMTAvYXZvaWRpbmctaW1hZ2UtZGVjb21wcmVzc2lvbi1zaWNrbmVzcy8=">https://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/<i class="fa fa-external-link-alt"></i></span></li>
</ul>
</li>
<li><p>同一个 URL 不会重复下载;</p>
</li>
<li><p>失效的 URL 不会被无限重试;</p>
</li>
<li><p>支持 GIF动画 及 WebP 格式;</p>
</li>
<li><p>开启 子线程 进行耗时操作，不阻塞主线程;</p>
</li>
</ul>
<blockquote>
<p>下载完图片的储存发生在SDWebImageManager里面(下载operation也是这里面创建的)，可以作为提前缓存图片的方式</p>
</blockquote>
<h1 id="SDWebImageContext"><a href="#SDWebImageContext" class="headerlink" title="SDWebImageContext"></a>SDWebImageContext</h1><h2 id="SDWebImageContextSetImageOperationKey"><a href="#SDWebImageContextSetImageOperationKey" class="headerlink" title="SDWebImageContextSetImageOperationKey"></a>SDWebImageContextSetImageOperationKey</h2><ol>
<li>简单来说，这个key对应的value用于指定当前的id<SDWebImageOperation>保存在NSMapTable中的key，后续的cancel、remove都需要通过这个key来找到对应的线程。</li>
<li>当指定同一个key加载图片时会先cancel之前存在的线程。</li>
<li>SDWebImageContextSetImageOperationKey并不是在所有地方使用都生效的。</li>
</ol>
<p>比如UIButton的sd_setImageWithURL:和sd_setBackgroundImageWithURL:系列方法。</p>
<p>在其内部需要通过这个这值来保存、区分不同状态(UIControlState)的图片加载线程，所以即使设置了SDWebImageContextSetImageOperationKey也会被覆盖：</p>
<h2 id="SDWebImageContextCustomManager"><a href="#SDWebImageContextCustomManager" class="headerlink" title="SDWebImageContextCustomManager"></a>SDWebImageContextCustomManager</h2><p>可以传入一个自定义的SDWebImageManager，默认使用[SDWebImageManager sharedManager]</p>
<h2 id="SDWebImageContextImageTransformer"><a href="#SDWebImageContextImageTransformer" class="headerlink" title="SDWebImageContextImageTransformer"></a>SDWebImageContextImageTransformer</h2><p>可以传入一个id<SDImageTransformer>类型用于转换处理加载出来的图片。</p>
<ol>
<li>在SDWebImageManager中也可以设置一个id<SDImageTransformer>默认为nil，但是只有SDWebImageContext没有配置SDWebImageContextImageTransformer，才会使用它。<br>也就是配置优先级 SDWebImageContext&gt;SDWebImageManager</li>
<li>如果设置了id<SDImageTransformer>不会缓存原始图片，只缓存处理后的图片。</li>
<li>对于同个图片、不同参数的id<SDImageTransformer>会被认为是不同的图片：会产生不同的缓存文件、会重复下载。</li>
</ol>
<h2 id="SDWebImageContextImageScaleFactor"><a href="#SDWebImageContextImageScaleFactor" class="headerlink" title="SDWebImageContextImageScaleFactor"></a>SDWebImageContextImageScaleFactor</h2><p>在NSData -&gt; UIImage时对图片放大比例，是个大于1的CGFloat值，默认值：</p>
<h2 id="SDWebImageContextStoreCacheType"><a href="#SDWebImageContextStoreCacheType" class="headerlink" title="SDWebImageContextStoreCacheType"></a>SDWebImageContextStoreCacheType</h2><p>定义图片缓存规则具体看 SDImageCacheType中的定义。</p>
<h2 id="SDWebImageContextDownloadRequestModifier"><a href="#SDWebImageContextDownloadRequestModifier" class="headerlink" title="SDWebImageContextDownloadRequestModifier"></a>SDWebImageContextDownloadRequestModifier</h2><p>可以传入一个id<SDWebImageDownloaderRequestModifier>，用于在加载图片前修改NSURLRequest。</p>
<ol>
<li>SDWebImageContextDownloadRequestModifier协议比较简单，只需要实现一个方法，返回一个修改后的NSURLRequest即可：</li>
<li>内建了一个SDWebImageDownloaderRequestModifier对象，可以使用Block方便的修改NSURLRequest</li>
</ol>
<h2 id="SDWebImageContextCacheKeyFilter"><a href="#SDWebImageContextCacheKeyFilter" class="headerlink" title="SDWebImageContextCacheKeyFilter"></a>SDWebImageContextCacheKeyFilter</h2><p>可以传入一个id<SDWebImageCacheKeyFilter>，指定图片的缓存key。</p>
<ol>
<li>SDWebImageCacheKeyFilter协议也比较简单，只需要实现一个方法，返回一个对应的缓存key字符串即可</li>
</ol>
<ul>
<li>(nullable NSString *)cacheKeyForURL:(nonnull NSURL *)url;</li>
</ul>
<ol start="2">
<li>内建了一个SDWebImageCacheKeyFilter对象，可以使用Block方便的返回缓存key</li>
</ol>
<h2 id="SDWebImageContextCacheSerializer"><a href="#SDWebImageContextCacheSerializer" class="headerlink" title="SDWebImageContextCacheSerializer"></a>SDWebImageContextCacheSerializer</h2><ol>
<li>可以传入一个id<SDWebImageCacheSerializer>，转换需要缓存的图片格式。</li>
<li>在SDWebImageManager中也可以设置一个id<SDWebImageCacheSerializer>默认为nil，但是只有SDWebImageContext没有配置SDWebImageContextCacheSerializer，才会使用它。<br>也就是配置优先级 SDWebImageContext&gt;SDWebImageManager</li>
<li>通常用于需要缓存的图片格式与下载的图片格式不相符的时候，如：下载的时候为了节约流量、减少下载时间使用了WebP格式，但是如果缓存也用WebP，每次从缓存中取图片都需要经过一次解压缩，这样是比较影响性能的，就可以使用id<SDWebImageCacheSerializer>，实现其中的协议方法：</li>
</ol>
<h2 id="SDWebImageContextLoaderCachedImage"><a href="#SDWebImageContextLoaderCachedImage" class="headerlink" title="SDWebImageContextLoaderCachedImage"></a>SDWebImageContextLoaderCachedImage</h2><p>可以传入一个UIImage的缓存图像。</p>
<ol>
<li>这个值比较特殊，它是定义在SDImageLoader.m中的。</li>
<li>这个值可以认为是SDWebImage是内部用来从SDWebImageManager向SDWebImageDownloader(id<SDImageLoader>)传递缓存图像的，自定义实现SDImageLoader协议可能会用到这个值，其他情况一般不会用到。</li>
<li>这个属性只有在 SDWebImageOptions包含SDWebImageRefreshCached策略时才生效，也就是他是SDWebImageRefreshCached这个策略的配套值。</li>
<li>SDWebImageRefreshCached这个策略用于那些图片URL是静态的（图片更新时URL是不变的，SD给的例子是 Facebook graph api profile pics），这个时候它会根据HTTP header的 cache-control 字段来控制缓存并且使用NSURLCache来缓存图片，SDWebImageDownloader(id<SDImageLoader>)中判断SDWebImageContextLoaderCachedImage存在并且策略是SDWebImageRefreshCached的情况，仍然会发起请求。</li>
</ol>
<h1 id="SDWebImageDownloader"><a href="#SDWebImageDownloader" class="headerlink" title="SDWebImageDownloader"></a>SDWebImageDownloader</h1><ul>
<li>self.URLOperations <SDWebImageDownloaderOperation>包含所有的下载队列</li>
</ul>
<p>这句话很重要 url和callback关联dic<br> // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.</p>
<p>downloadOperationCancelToken 返回绑定当前operation的callback、progressBlock, 是一个可变字典</p>
<blockquote>
<p>operation.loaderOperation = SDWebImageDownloadToken</p>
</blockquote>
<ul>
<li>addHandlersForProgress</li>
</ul>
<p>其实是向 SDWebImageDownloaderOperation 里面的callbackBlocks注册回调callback</p>
<p>SDWebImageDownloadToken 其实包含</p>
<ul>
<li>url</li>
<li>request</li>
<li>downloadOperationCancelToken (这是一个dic，又包含)<ul>
<li>callback</li>
<li>progressBlock</li>
</ul>
</li>
</ul>
<h2 id="cancel的时候"><a href="#cancel的时候" class="headerlink" title="cancel的时候"></a>cancel的时候</h2><p>其中一个队列SDWebImageDownloaderOperation，包含所有url对应的回调</p>
<ul>
<li>self.callbackBlocks </li>
</ul>
<h2 id="options参数："><a href="#options参数：" class="headerlink" title="options参数："></a>options参数：</h2><table>
<thead>
<tr>
<th>SDWebImageOptions属性</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>SDWebImageRetryFailed</td>
<td>默认情况下，如果一个url在下载的时候失败了,那么这个url会被加入黑名单并且library不会尝试再次下载,这个flag会阻止library把失败的url加入黑名单</td>
</tr>
<tr>
<td>SDWebImageLowPriorit</td>
<td>默认情况下，图片会在交互发生的时候下载(例如你滑动tableview的时候),这个flag会禁止这个特性,导致的结果就是在scrollview减速的时候,才会开始下载</td>
</tr>
<tr>
<td>SDWebImageProgressiveLoad</td>
<td>这个flag启动渐进式下载图像，类似浏览器加载图像那样逐步显示，默认情况下，图像仅是在下载完成后显示</td>
</tr>
<tr>
<td>SDWebImageRefreshCached</td>
<td>一个图片缓存了，还是会重新请求.并且缓存侧略依据NSURLCache而不是SDWebImage。即在URL没变但是服务器图片发生更新时使用</td>
</tr>
<tr>
<td>SDWebImageContinueInBackground</td>
<td>启动后台下载，实现原理是通过向系统询问后台的额外时间来完成请求的。 如果后台任务到期，则操作将被取消。</td>
</tr>
<tr>
<td>SDWebImageHandleCookies</td>
<td>当设置了<code>NSMutableURLRequest.HTTPShouldHandleCookies = YES</code>时，可以控制存储NSHTTPCookieStorage中的cookie</td>
</tr>
<tr>
<td>SDWebImageAllowInvalidSSLCertificates</td>
<td>允许不安全的SSL证书，用于测试环境，在正式环境中谨慎使用</td>
</tr>
<tr>
<td>SDWebImageHighPriority</td>
<td>默认情况下,image在装载的时候是按照他们在队列中的顺序装载的(就是先进先出)。这个flag会把他们移动到队列的前端,并且立刻装载,而不是等到当前队列装载的时候再装载。</td>
</tr>
<tr>
<td>SDWebImageDelayPlaceholder</td>
<td>默认情况下,占位图会在图片下载的时候显示.这个flag开启会延迟占位图显示的时间,等到图片下载完成之后才会显示占位图.</td>
</tr>
<tr>
<td>SDWebImageTransformAnimatedImage</td>
<td>通常不会在可动画的图像上调用 <code>transformDownloadedImage</code> 代理方法，因为大多数转换代码会破坏动画文件，这个flag为尝试转换</td>
</tr>
<tr>
<td>SDWebImageAvoidAutoSetImage</td>
<td>图片在下载后被加载到imageView。但是在一些情况下，我们想要设置一下图片（引用一个滤镜或者加入透入动画）这个flag来手动的设置图片在下载图片成功后</td>
</tr>
<tr>
<td>SDWebImageScaleDownLargeImages</td>
<td>默认情况下，图像将根据其原始大小进行解码。 在iOS上，此flat会将图片缩小到与设备的受限内存兼容的大小。    但如果设置了<code>SDWebImageAvoidDecodeImage</code>则此flat不起作用。 如果设置了<code>SDWebImageProgressiveLoad</code>它将被忽略。</td>
</tr>
<tr>
<td>SDWebImageQueryMemoryData</td>
<td>默认情况下，当图像已缓存在内存中时，我们不会查询图像数据。 此flat则强制查询图像数据。 此查询是异步的，除非指定<code>SDWebImageQueryMemoryDataSync</code></td>
</tr>
<tr>
<td>SDWebImageQueryMemoryDataSync</td>
<td>结合<code>SDWebImageQueryMemoryData</code>设置同步查询图像数据（一般不建议这么使用，除非是在同一个<code>runloop</code>里避免单元格复用时发生闪现）</td>
</tr>
<tr>
<td>SDWebImageQueryDiskDataSync</td>
<td>如果内存查询没有的时候，强制同步磁盘查询（这三个查询可以组合使用，一般不建议这么使用，除非是在同一个<code>runloop</code>里避免单元格复用时发生闪现）</td>
</tr>
<tr>
<td>SDWebImageFromCacheOnly</td>
<td>默认情况下，当缓存丢失时，SD将从网络下载图像。 此flat可以防止这样，使其仅从缓存加载。</td>
</tr>
<tr>
<td>SDWebImageFromLoaderOnly</td>
<td>默认情况下，SD在下载之前先从缓存中查找，此flat可以防止这样，使其仅从网络下载</td>
</tr>
<tr>
<td>SDWebImageForceTransition</td>
<td>默认情况下，SD在图像加载完成后使用<code>SDWebImageTransition</code>进行某些视图转换，此转换仅适用于从网络下载图像。 此flat可以强制为内存和磁盘缓存应用视图转换。</td>
</tr>
<tr>
<td>SDWebImageAvoidDecodeImage</td>
<td>默认情况下，SD在查询缓存和从网络下载时会在后台解码图像，这有助于提高性能，因为在屏幕上渲染图像时，需要首先对其进行解码。这发生在<code>Core Animation</code>的主队列中。然而此过程也可能会增加内存使用量。 如果由于过多的内存消耗而遇到问题，可以用此flat禁止解码图像。</td>
</tr>
<tr>
<td>SDWebImageDecodeFirstFrameOnly</td>
<td>默认情况下，SD会解码动画图像，该flat强制只解码第一帧并生成静态图。</td>
</tr>
<tr>
<td>SDWebImagePreloadAllFrames</td>
<td>默认情况下，对于<code>SDAnimatedImage</code>，SD会在渲染过程中解码动画图像帧以减少内存使用量。 但是用户可以指定将所有帧预加载到内存中，以便在大量imageView共享动画图像时降低CPU使用率。这实际上会在后台队列中触发<code>preloadAllAnimatedImageFrames</code>（仅限磁盘缓存和下载）。</td>
</tr>
</tbody></table>
<h1 id="SDImageTransformer的类型"><a href="#SDImageTransformer的类型" class="headerlink" title="SDImageTransformer的类型"></a>SDImageTransformer的类型</h1><h1 id="SDImageCache"><a href="#SDImageCache" class="headerlink" title="SDImageCache"></a>SDImageCache</h1><p>NSCache</p>
<ul>
<li>自动删除机制：当系统内存紧张时，NSCache会自动删除一些缓存对象</li>
<li>线程安全：从不同线程中对同一个 NSCache 对象进行增删改查时，不需要加锁</li>
<li>不同于 NSMutableDictionary，NSCache存储对象时不会对 key 进行 copy 操作</li>
</ul>
<h1 id="小Tip"><a href="#小Tip" class="headerlink" title="小Tip"></a>小Tip</h1><ol>
<li>运行时存取关联对象：</li>
<li>数组的写操作需要加锁（多线程访问，避免覆写）<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;给self.runningOperations加锁</span><br><span class="line">&#x2F;&#x2F;self.runningOperations数组的添加操作</span><br><span class="line">    @synchronized (self.runningOperations) &#123;</span><br><span class="line">        [self.runningOperations addObject:operation];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;self.runningOperations数组的删除操作</span><br><span class="line">- (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation &#123;</span><br><span class="line">    @synchronized (self.runningOperations) &#123;</span><br><span class="line">        if (operation) &#123;</span><br><span class="line">            [self.runningOperations removeObject:operation];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>确保在主线程的宏：<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">dispatch_main_async_safe(^&#123;</span><br><span class="line">                  &#x2F;&#x2F;将下面这段代码放在主线程中</span><br><span class="line">            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;宏定义：</span><br><span class="line">#define dispatch_main_async_safe(block)\</span><br><span class="line">    if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) &#x3D;&#x3D; 0) &#123;\</span><br><span class="line">        block();\</span><br><span class="line">    &#125; else &#123;\</span><br><span class="line">        dispatch_async(dispatch_get_main_queue(), block);\</span><br><span class="line">    &#125;</span><br><span class="line">#endif</span><br></pre></td></tr></table></figure></li>
<li>设置不能为nil的参数<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader &#123;</span><br><span class="line">    if ((self &#x3D; [super init])) &#123;</span><br><span class="line">        _imageCache &#x3D; cache;</span><br><span class="line">        _imageDownloader &#x3D; downloader;</span><br><span class="line">        _failedURLs &#x3D; [NSMutableSet new];</span><br><span class="line">        _runningOperations &#x3D; [NSMutableArray new];</span><br><span class="line">    &#125;</span><br><span class="line">    return self;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>容错，强制转换类型<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">if ([url isKindOfClass:NSString.class]) &#123;</span><br><span class="line">        url &#x3D; [NSURL URLWithString:(NSString *)url];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ol>
<h1 id="UIImageView分类方法，分流到了UIView的分类方法，注释如下"><a href="#UIImageView分类方法，分流到了UIView的分类方法，注释如下" class="headerlink" title="UIImageView分类方法，分流到了UIView的分类方法，注释如下"></a>UIImageView分类方法，分流到了UIView的分类方法，注释如下</h1><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">	使用图片的 URL 和可选的 placeholder image 设置 imageView 的 image </span></span><br><span class="line"><span class="comment">	</span></span><br><span class="line"><span class="comment">	下载是异步和缓存的</span></span><br><span class="line"><span class="comment">	</span></span><br><span class="line"><span class="comment">	@param url image的URL</span></span><br><span class="line"><span class="comment">	@param placeholder 初始图像，直至image数据请求完成</span></span><br><span class="line"><span class="comment">	@param options image下载的时候的选择项-SDWebImageOptions，下方有详细介绍</span></span><br><span class="line"><span class="comment">	@param context 为了补充options枚举没有的选择想，下方有详细介绍</span></span><br><span class="line"><span class="comment">	@param setImageBlock 用于自定义设置image</span></span><br><span class="line"><span class="comment">	@param progressBlock 在图片下载ing状态调用，注：在后台队列上执行</span></span><br><span class="line"><span class="comment">	@param completedBlock 在操作完成后调用，返回类型为</span></span><br><span class="line"><span class="comment">	completedBlock(image, data, error, cacheType, finished, url)</span></span><br><span class="line"><span class="comment">	</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)sd_internalSetImageWithURL:(<span class="keyword">nullable</span> <span class="built_in">NSURL</span> *)url</span><br><span class="line">                  placeholderImage:(<span class="keyword">nullable</span> <span class="built_in">UIImage</span> *)placeholder</span><br><span class="line">                           options:(SDWebImageOptions)options</span><br><span class="line">                           context:(<span class="keyword">nullable</span> SDWebImageContext *)context</span><br><span class="line">                     setImageBlock:(<span class="keyword">nullable</span> SDSetImageBlock)setImageBlock</span><br><span class="line">                          progress:(<span class="keyword">nullable</span> SDImageLoaderProgressBlock)progressBlock</span><br><span class="line">                         completed:(<span class="keyword">nullable</span> SDInternalCompletionBlock)completedBlock &#123;</span><br><span class="line">    <span class="comment">//将可变对象copy为不可变的</span></span><br><span class="line">    context = [context <span class="keyword">copy</span>]; <span class="comment">// copy to avoid mutable object</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">//取出context设置中的operationKey</span></span><br><span class="line">    <span class="built_in">NSString</span> *validOperationKey = context[SDWebImageContextSetImageOperationKey];</span><br><span class="line">    <span class="comment">//如果operationKey为nil，就采用他自身的类的字符串作为key</span></span><br><span class="line">    <span class="keyword">if</span> (!validOperationKey) &#123;</span><br><span class="line">        validOperationKey = <span class="built_in">NSStringFromClass</span>([<span class="keyword">self</span> <span class="keyword">class</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//取消之前绑定的operation，保证没有当前正在进行的异步下载操作, 使它不会与即将进行的操作发生冲突</span></span><br><span class="line">    [<span class="keyword">self</span> sd_cancelImageLoadOperationWithKey:validOperationKey];</span><br><span class="line">    <span class="comment">//为自身绑定一个URL</span></span><br><span class="line">    <span class="keyword">self</span>.sd_imageURL = url;</span><br><span class="line">    <span class="comment">//如果不是延迟显示placeholder的情况</span></span><br><span class="line">    <span class="keyword">if</span> (!(options &amp; SDWebImageDelayPlaceholder)) &#123;</span><br><span class="line">    	 <span class="comment">//dispatch_main_async_safe 异步线程安全，下面有源码介绍</span></span><br><span class="line">        dispatch_main_async_safe(^&#123;</span><br><span class="line">        	 <span class="comment">//在图片下载下来之前，添加临时的占位图</span></span><br><span class="line">            [<span class="keyword">self</span> sd_setImage:placeholder imageData:<span class="literal">nil</span> basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (url) &#123;</span><br><span class="line">        <span class="comment">// reset the progress</span></span><br><span class="line">        <span class="keyword">self</span>.sd_imageProgress.totalUnitCount = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">self</span>.sd_imageProgress.completedUnitCount = <span class="number">0</span>;</span><br><span class="line">        </span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> SD_UIKIT || SD_MAC</span></span><br><span class="line">        <span class="comment">// check and start image indicator</span></span><br><span class="line">        <span class="comment">// 检查是否设置了image indicator，如果有则启动，下面有方法的源码</span></span><br><span class="line">        [<span class="keyword">self</span> sd_startImageIndicator];</span><br><span class="line">        <span class="keyword">id</span>&lt;SDWebImageIndicator&gt; imageIndicator = <span class="keyword">self</span>.sd_imageIndicator;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">        <span class="comment">// 判断context中是否自定义了manager，如果没有则使用默认的</span></span><br><span class="line">        SDWebImageManager *manager = context[SDWebImageContextCustomManager];</span><br><span class="line">        <span class="keyword">if</span> (!manager) &#123;</span><br><span class="line">            manager = [SDWebImageManager sharedManager];</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 设置image加载进度Block（已接收size，预计总size，image的URL）</span></span><br><span class="line">        __<span class="keyword">weak</span> __<span class="keyword">typeof</span>(<span class="keyword">self</span>)wself = <span class="keyword">self</span>;</span><br><span class="line">        SDImageLoaderProgressBlock combinedProgressBlock = ^(<span class="built_in">NSInteger</span> receivedSize, <span class="built_in">NSInteger</span> expectedSize, <span class="built_in">NSURL</span> * _Nullable targetURL) &#123;</span><br><span class="line">        	<span class="comment">// 使用__strong __typeof是防止self在这个执行过程中释放，下方有详细介绍</span></span><br><span class="line">            __<span class="keyword">strong</span> __<span class="keyword">typeof</span> (wself) sself = wself;</span><br><span class="line">            <span class="built_in">NSProgress</span> *imageProgress = sself.sd_imageProgress;</span><br><span class="line">            imageProgress.totalUnitCount = expectedSize;</span><br><span class="line">            imageProgress.completedUnitCount = receivedSize;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> SD_UIKIT || SD_MAC</span></span><br><span class="line">				<span class="comment">//加载指示器是否实现了updateIndicatorProgress方法</span></span><br><span class="line">            <span class="keyword">if</span> ([imageIndicator respondsToSelector:<span class="keyword">@selector</span>(updateIndicatorProgress:)]) &#123;</span><br><span class="line">                <span class="keyword">double</span> progress = imageProgress.fractionCompleted;</span><br><span class="line">                <span class="built_in">dispatch_async</span>(dispatch_get_main_queue(), ^&#123;</span><br><span class="line">                    [imageIndicator updateIndicatorProgress:progress];</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span>			<span class="comment">//返回image加载进度Block</span></span></span><br><span class="line">            <span class="keyword">if</span> (progressBlock) &#123;</span><br><span class="line">                progressBlock(receivedSize, expectedSize, targetURL);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//下载图片</span></span><br><span class="line">        <span class="keyword">id</span> &lt;SDWebImageOperation&gt; operation = [manager loadImageWithURL:url options:options context:context progress:combinedProgressBlock completed:^(<span class="built_in">UIImage</span> *image, <span class="built_in">NSData</span> *data, <span class="built_in">NSError</span> *error, SDImageCacheType cacheType, <span class="built_in">BOOL</span> finished, <span class="built_in">NSURL</span> *imageURL) &#123;</span><br><span class="line">            __<span class="keyword">strong</span> __<span class="keyword">typeof</span> (wself) sself = wself;</span><br><span class="line">            <span class="keyword">if</span> (!sself) &#123; <span class="keyword">return</span>; &#125;</span><br><span class="line">           </span><br><span class="line">            <span class="comment">// 如果progress没有更新，则标记其为完成状态</span></span><br><span class="line">            <span class="keyword">if</span> (finished &amp;&amp; !error &amp;&amp; sself.sd_imageProgress.totalUnitCount == <span class="number">0</span> &amp;&amp; sself.sd_imageProgress.completedUnitCount == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">//const int64_t SDWebImageProgressUnitCountUnknown = 1LL; （LL是 long long 类型的缩写）</span></span><br><span class="line">                sself.sd_imageProgress.totalUnitCount = SDWebImageProgressUnitCountUnknown;</span><br><span class="line">                sself.sd_imageProgress.completedUnitCount = SDWebImageProgressUnitCountUnknown;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> SD_UIKIT || SD_MAC</span></span><br><span class="line">            <span class="comment">// 检查并停止 image indicator</span></span><br><span class="line">            <span class="keyword">if</span> (finished) &#123;</span><br><span class="line">                [<span class="keyword">self</span> sd_stopImageIndicator];</span><br><span class="line">            &#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">            <span class="comment">// 下载完成后是否自动加载图片 (options &amp; SDWebImageAvoidAutoSetImage)根据枚举名取枚举中的值，get了</span></span><br><span class="line">            <span class="built_in">BOOL</span> shouldCallCompletedBlock = finished || (options &amp; SDWebImageAvoidAutoSetImage);</span><br><span class="line">            <span class="built_in">BOOL</span> shouldNotSetImage = ((image &amp;&amp; (options &amp; SDWebImageAvoidAutoSetImage)) ||</span><br><span class="line">                                      (!image &amp;&amp; !(options &amp; SDWebImageDelayPlaceholder)));</span><br><span class="line">            SDWebImageNoParamsBlock callCompletedBlockClojure = ^&#123;</span><br><span class="line">                <span class="keyword">if</span> (!sself) &#123; <span class="keyword">return</span>; &#125;</span><br><span class="line">                <span class="keyword">if</span> (!shouldNotSetImage) &#123;</span><br><span class="line">                    [sself sd_setNeedsLayout];</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 设置completedBlock</span></span><br><span class="line">                <span class="keyword">if</span> (completedBlock &amp;&amp; shouldCallCompletedBlock) &#123;</span><br><span class="line">                    completedBlock(image, data, error, cacheType, finished, url);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// case 1a: we got an image, but the SDWebImageAvoidAutoSetImage flag is set</span></span><br><span class="line">            <span class="comment">// OR</span></span><br><span class="line">            <span class="comment">// case 1b: we got no image and the SDWebImageDelayPlaceholder is not set</span></span><br><span class="line">            <span class="keyword">if</span> (shouldNotSetImage) &#123;</span><br><span class="line">                dispatch_main_async_safe(callCompletedBlockClojure);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="built_in">UIImage</span> *targetImage = <span class="literal">nil</span>;</span><br><span class="line">            <span class="built_in">NSData</span> *targetData = <span class="literal">nil</span>;</span><br><span class="line">            <span class="keyword">if</span> (image) &#123;</span><br><span class="line">                <span class="comment">// case 2a: we got an image and the SDWebImageAvoidAutoSetImage is not set</span></span><br><span class="line">                targetImage = image;</span><br><span class="line">                targetData = data;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (options &amp; SDWebImageDelayPlaceholder) &#123;</span><br><span class="line">                <span class="comment">// case 2b: we got no image and the SDWebImageDelayPlaceholder flag is set</span></span><br><span class="line">                targetImage = placeholder;</span><br><span class="line">                targetData = <span class="literal">nil</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> SD_UIKIT || SD_MAC</span></span><br><span class="line">            <span class="comment">// check whether we should use the image transition</span></span><br><span class="line">            <span class="comment">// 检查image的过渡动画效果</span></span><br><span class="line">            SDWebImageTransition *transition = <span class="literal">nil</span>;</span><br><span class="line">            <span class="keyword">if</span> (finished &amp;&amp; (options &amp; SDWebImageForceTransition || cacheType == SDImageCacheTypeNone)) &#123;</span><br><span class="line">                transition = sself.sd_imageTransition;</span><br><span class="line">            &#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">				<span class="comment">// 设置image的过渡动画效果</span></span><br><span class="line">            dispatch_main_async_safe(^&#123;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> SD_UIKIT || SD_MAC</span></span><br><span class="line">                [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL];</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">                [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:cacheType imageURL:imageURL];</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">                callCompletedBlockClojure();</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;];</span><br><span class="line">        <span class="comment">//在操作缓存字典（operationDictionary）里添加operation，表示当前的操作正在进行，源码见下</span></span><br><span class="line">        [<span class="keyword">self</span> sd_setImageLoadOperation:operation forKey:validOperationKey];</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    		<span class="comment">// 没有url，停止Image Indicator</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> SD_UIKIT || SD_MAC</span></span><br><span class="line">        [<span class="keyword">self</span> sd_stopImageIndicator];</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">        dispatch_main_async_safe(^&#123;</span><br><span class="line">            <span class="keyword">if</span> (completedBlock) &#123;</span><br><span class="line">                <span class="built_in">NSError</span> *error = [<span class="built_in">NSError</span> errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidURL userInfo:@&#123;<span class="built_in">NSLocalizedDescriptionKey</span> : <span class="string">@&quot;Image url is nil&quot;</span>&#125;];</span><br><span class="line">                completedBlock(<span class="literal">nil</span>, <span class="literal">nil</span>, error, SDImageCacheTypeNone, <span class="literal">YES</span>, url);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/interview/applaunchspeed.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/interview/applaunchspeed.html" class="post-title-link" itemprop="url">app启动优化</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-07-15 11:26:39" itemprop="dateCreated datePublished" datetime="2017-07-15T11:26:39+08:00">2017-07-15</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/" itemprop="url" rel="index"><span itemprop="name">面试总结</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/app%E5%90%AF%E5%8A%A8%E4%BC%98%E5%8C%96/" itemprop="url" rel="index"><span itemprop="name">app启动优化</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="main-调用之前的加载过程"><a href="#main-调用之前的加载过程" class="headerlink" title="main()调用之前的加载过程"></a>main()调用之前的加载过程</h1><p>App开始启动后，系统首先加载可执行文件（自身App的所有.o文件的集合），然后加载动态链接库dyld，dyld是一个专门用来加载动态链接库的库。 执行从dyld开始，dyld从可执行文件的依赖开始, 递归加载所有的依赖动态链接库。<br>动态链接库包括：iOS 中用到的所有系统 framework，加载OC runtime方法的libobjc，系统级别的libSystem，例如libdispatch(GCD)和libsystem_blocks (Block)。<br>其实无论对于系统的动态链接库还是对于App本身的可执行文件而言，他们都算是image（镜像），而每个App都是以image(镜像)为单位进行加载的，那么image究竟包括哪些呢？</p>
<h2 id="系统使用动态链接有几点好处："><a href="#系统使用动态链接有几点好处：" class="headerlink" title="系统使用动态链接有几点好处："></a>系统使用动态链接有几点好处：</h2><p>代码共用：很多程序都动态链接了这些 lib，但它们在内存和磁盘中中只有一份。 易于维护：由于被依赖的 lib 是程序执行时才链接的，所以这些 lib 很容易做更新，比如libSystem.dylib 是 libSystem.B.dylib 的替身，哪天想升级直接换成libSystem.C.dylib 然后再替换替身就行了。 减少可执行文件体积：相比静态链接，动态链接在编译时不需要打进去，所以可执行文件的体积要小很多。</p>
<img src="/blogwebpage/images/iOS/applaunch_macho.jpeg" class="" title="applaunch_macho">

<p>如上图所示，不同进程之间共用系统dylib的_TEXT区，但是各自维护对应的_DATA区。<br>所有动态链接库和我们App中的静态库.a和所有类文件编译后的.o文件最终都是由dyld(the dynamic link editor)，Apple的动态链接器来加载到内存中。每个image都是由一个叫做ImageLoader的类来负责加载（一一对应），那么ImageLoader又是什么呢？</p>
<h2 id="什么是ImageLoader"><a href="#什么是ImageLoader" class="headerlink" title="什么是ImageLoader"></a>什么是ImageLoader</h2><p>image 表示一个二进制文件(可执行文件或 so 文件)，里面是被编译过的符号、代码等，所以 ImageLoader 作用是将这些文件加载进内存，且每一个文件对应一个ImageLoader实例来负责加载。<br>两步走： 在程序运行时它先将动态链接的 image 递归加载 (也就是上面测试栈中一串的递归调用的时刻)。 再从可执行文件 image 递归加载所有符号。<br>当然所有这些都发生在我们真正的main函数执行前。</p>
<h2 id="动态链接库加载的具体流程"><a href="#动态链接库加载的具体流程" class="headerlink" title="动态链接库加载的具体流程"></a>动态链接库加载的具体流程</h2><p>动态链接库的加载步骤具体分为5步：</p>
<ul>
<li><p>load dylibs image 读取库镜像文件</p>
</li>
<li><p>Rebase image</p>
</li>
<li><p>Bind image</p>
</li>
<li><p>Objc setup</p>
</li>
<li><p>initializers</p>
</li>
</ul>
<h3 id="load-dylibs-image"><a href="#load-dylibs-image" class="headerlink" title="load dylibs image"></a>load dylibs image</h3><p>在每个动态库的加载过程中， dyld需要：</p>
<ul>
<li>分析所依赖的动态库</li>
<li>找到动态库的mach-o文件</li>
<li>打开文件</li>
<li>验证文件</li>
<li>在系统核心注册文件签名</li>
<li>对动态库的每一个segment调用mmap()</li>
</ul>
<p>通常的，一个App需要加载100到400个dylibs， 但是其中的系统库被优化，可以很快的加载。 针对这一步骤的优化有：</p>
<ul>
<li>减少非系统库的依赖</li>
<li>合并非系统库</li>
<li>使用静态资源，比如把代码加入主程序</li>
</ul>
<h3 id="rebase-bind"><a href="#rebase-bind" class="headerlink" title="rebase/bind"></a>rebase/bind</h3><p>由于ASLR(address space layout randomization)的存在，可执行文件和动态链接库在虚拟内存中的加载地址每次启动都不固定，所以需要这2步来修复镜像中的资源指针，来指向正确的地址。 rebase修复的是指向当前镜像内部的资源指针； 而bind指向的是镜像外部的资源指针。<br>rebase步骤先进行，需要把镜像读入内存，并以page为单位进行加密验证，保证不会被篡改，所以这一步的瓶颈在IO。bind在其后进行，由于要查询符号表，来指向跨镜像的资源，加上在rebase阶段，镜像已被读入和加密验证，所以这一步的瓶颈在于CPU计算。<br>通过命令行可以查看相关的资源指针:</p>
<blockquote>
<p>xcrun dyldinfo -rebase -bind -lazy_bind myApp.App/myApp</p>
</blockquote>
<p>优化该阶段的关键在于减少__DATA segment中的指针数量。我们可以优化的点有：</p>
<ul>
<li><p>减少Objc类数量， 减少selector数量</p>
</li>
<li><p>减少C++虚函数数量</p>
</li>
<li><p>转而使用swift stuct（其实本质上就是为了减少符号的数量）</p>
</li>
</ul>
<h3 id="Objc-setup"><a href="#Objc-setup" class="headerlink" title="Objc setup"></a>Objc setup</h3><p>这一步主要工作是:</p>
<ul>
<li>注册Objc类 (class registration)</li>
<li>把category的定义插入方法列表 (category registration)</li>
<li>保证每一个selector唯一 (selctor uniquing)</li>
</ul>
<p>由于之前2步骤的优化，这一步实际上没有什么可做的。</p>
<h3 id="initializers"><a href="#initializers" class="headerlink" title="initializers"></a>initializers</h3><p>以上三步属于静态调整(fix-up)，都是在修改__DATA segment中的内容，而这里则开始动态调整，开始在堆和堆栈中写入内容。 在这里的工作有：</p>
<ul>
<li>Objc的+load()函数</li>
<li>C++的构造函数属性函数 形如attribute((constructor)) void DoSomeInitializationWork()</li>
<li>非基本类型的C++静态全局变量的创建(通常是类或结构体)(non-trivial initializer) 比如一个全局静态结构体的构建，如果在构造函数中有繁重的工作，那么会拖慢启动速度</li>
</ul>
<p>Objc的load函数和C++的静态构造函数采用由底向上的方式执行，来保证每个执行的方法，都可以找到所依赖的动态库。</p>
<img src="/blogwebpage/images/iOS/applaunch_dyld.jpeg" class="" title="applaunch_dyld">

<p>上图是在自定义的类XXViewController的+load方法断点的调用堆栈，清楚的看到整个调用栈和顺序：</p>
<ul>
<li>dyld 开始将程序二进制文件初始化</li>
<li>交由 ImageLoader 读取 image，其中包含了我们的类、方法等各种符号</li>
<li>由于 runtime 向 dyld 绑定了回调，当 image 加载到内存后，dyld 会通知 runtime 进行处理</li>
<li>runtime 接手后调用 mapimages 做解析和处理，接下来 loadimages 中调用 callloadmethods 方法，遍历所有加载进来的 Class，按继承层级依次调用 Class 的 +load 方法和其 Category 的 +load 方法</li>
</ul>
<p>至此，可执行文件中和动态库所有的符号(Class，Protocol，Selector，IMP，…)都已经按格式成功加载到内存中，被 runtime 所管理，再这之后，runtime 的那些方法(动态添加 Class、swizzle 等等才能生效)。<br>整个事件由 dyld 主导，完成运行环境的初始化后，配合 ImageLoader 将二进制文件按格式加载到内存， 动态链接依赖库，并由 runtime 负责加载成 objc 定义的结构，所有初始化工作结束后，dyld 调用真正的 main 函数。<br>如果程序刚刚被运行过，那么程序的代码会被dyld缓存，因此即使杀掉进程再次重启加载时间也会相对快一点，如果长时间没有启动或者当前dyld的缓存已经被其他应用占据，那么这次启动所花费的时间就要长一点</p>
<h2 id="main-之前的加载时间如何衡量"><a href="#main-之前的加载时间如何衡量" class="headerlink" title="main()之前的加载时间如何衡量"></a>main()之前的加载时间如何衡量</h2><p>那么问题就来了，那怎么衡量main()之前也就是time1的耗时呢，苹果官方提供了一种方法，那就是在真机调试的时候勾选DYLD_PRINT_STATISTICS选项。</p>
<p>会得到如下形式的输出：</p>
<figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Total</span> pre-main time:  <span class="number">42</span>.<span class="number">89</span> milliseconds (<span class="number">100</span>.<span class="number">0</span>%)</span><br><span class="line">         <span class="attribute">dylib</span> loading time:  <span class="number">38</span>.<span class="number">75</span> milliseconds (<span class="number">90</span>.<span class="number">3</span>%)</span><br><span class="line">        <span class="attribute">rebase</span>/binding time: <span class="number">411015771</span>.<span class="number">6</span> seconds (<span class="number">80806899</span>.<span class="number">0</span>%)</span><br><span class="line">            <span class="attribute">ObjC</span> setup time:   <span class="number">3</span>.<span class="number">69</span> milliseconds (<span class="number">8</span>.<span class="number">6</span>%)</span><br><span class="line">           <span class="attribute">initializer</span> time:  <span class="number">17</span>.<span class="number">68</span> milliseconds (<span class="number">41</span>.<span class="number">2</span>%)</span><br><span class="line">           <span class="attribute">slowest</span> intializers :</span><br><span class="line">             <span class="attribute">libSystem</span>.B.dylib :   <span class="number">3</span>.<span class="number">07</span> milliseconds (<span class="number">7</span>.<span class="number">1</span>%)</span><br><span class="line">   <span class="attribute">libBacktraceRecording</span>.dylib :   <span class="number">2</span>.<span class="number">43</span> milliseconds (<span class="number">5</span>.<span class="number">6</span>%)</span><br><span class="line">    <span class="attribute">libMainThreadChecker</span>.dylib :   <span class="number">9</span>.<span class="number">07</span> milliseconds (<span class="number">21</span>.<span class="number">1</span>%)</span><br><span class="line">                    <span class="attribute">AppProject</span> :   <span class="number">1</span>.<span class="number">56</span> milliseconds (<span class="number">3</span>.<span class="number">6</span>%)</span><br></pre></td></tr></table></figure>

<p>由此可见，最多的用时还是在image加载和OC类的初始化，共占用总时长的79.3%，精简framework的引入和OC类有优化的空间。<br>总结一下：对于main()调用之前的耗时我们可以优化的点有：</p>
<ul>
<li><p>减少不必要的framework，因为动态链接比较耗时</p>
</li>
<li><p>check framework应当设为optional和required，如果该framework在当前App支持的所有iOS系统版本都存在，那么就设为required，否则就设为optional，因为optional会有些额外的检查</p>
</li>
<li><p>合并或者删减一些OC类，关于清理项目中没用到的类，使用工具AppCode代码检查功能，查到当前项目中没有用到的类如下：</p>
</li>
<li><p>删减一些无用的静态变量</p>
</li>
<li><p>删减没有被调用到或者已经废弃的方法</p>
  <figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">    方法见：http:<span class="regexp">//</span>stackoverflow.com<span class="regexp">/questions/</span><span class="number">35233564</span>/how-to-find-unused-code-<span class="keyword">in</span>-xcode-<span class="number">7</span></span><br><span class="line">https:<span class="regexp">//</span>developer.Apple.com<span class="regexp">/library/i</span>os<span class="regexp">/documentation/</span>ToolsLanguages<span class="regexp">/Conceptual/</span>Xcode_Overview/CheckingCodeCoverage.html</span><br></pre></td></tr></table></figure></li>
<li><p>将不必须在+load方法中做的事情延迟到+initialize中</p>
</li>
<li><p>尽量不要用C++虚函数(创建虚函数表有开销)</p>
</li>
</ul>
<h1 id="main-调用之后的加载时间"><a href="#main-调用之后的加载时间" class="headerlink" title="main()调用之后的加载时间"></a>main()调用之后的加载时间</h1><p>在main()被调用之后，App的主要工作就是初始化必要的服务，显示首页内容等。而我们的优化也是围绕如何能够快速展现首页来开展。 App通常在AppDelegate类中的- (BOOL)Application:(UIApplication *)Application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中创建首页需要展示的view，然后在当前runloop的末尾，主动调用CA::Transaction::commit完成视图的渲染。<br>而视图的渲染主要涉及三个阶段：</p>
<ul>
<li>准备阶段 这里主要是图片的解码</li>
<li>布局阶段 首页所有UIView的- (void)layoutSubViews()运行</li>
<li>绘制阶段 首页所有UIView的- (void)drawRect:(CGRect)rect运行<br>再加上启动之后必要服务的启动、必要数据的创建和读取，这些就是我们可以尝试优化的地方</li>
</ul>
<p>因此，对于main()函数调用之前我们可以优化的点有：</p>
<ul>
<li>不使用xib，直接视用代码加载首页视图</li>
<li>NSUserDefaults实际上是在Library文件夹下会生产一个plist文件，如果文件太大的话一次能读取到内存中可能很耗时，这个影响需要评估，如果耗时很大的话需要拆分(需考虑老版本覆盖安装兼容问题)</li>
<li>每次用NSLog方式打印会隐式的创建一个Calendar，因此需要删减启动时各业务方打的log，或者仅仅针对内测版输出log</li>
<li>梳理应用启动时发送的所有网络请求，是否可以统一在异步线程请求</li>
</ul>
<h1 id="如何找到拖慢启动应用时长的瓶颈"><a href="#如何找到拖慢启动应用时长的瓶颈" class="headerlink" title="如何找到拖慢启动应用时长的瓶颈"></a>如何找到拖慢启动应用时长的瓶颈</h1><p>为了找到瓶颈，我们在启动之后的didFinishLauhcning方法开始执行到首页列表页的NewsListViewController的viewDidAppear方法，几乎每个可能比较耗时的流程进行拆分和统计，得到统计数据之后发现： 主要耗时在首页UI构造和渲染(storyboard加载，tabBar/topBar渲染，开屏广告加载/cell注册/日志模块初始化这几个步骤)。</p>
<h1 id="具体优化点"><a href="#具体优化点" class="headerlink" title="具体优化点"></a>具体优化点</h1><p>因此，可以优化的点如下：</p>
<ul>
<li>纯代码方式而不是storyboard加载首页UI。</li>
<li>对didFinishLaunching里的函数考虑能否挖掘可以延迟加载或者懒加载，需要与各个业务方pm和rd共同check 对于一些已经下线的业务，删减冗余代码。<br>对于一些与UI展示无关的业务，如微博认证过期检查、图片最大缓存空间设置等做延迟加载</li>
<li>对实现了+load()方法的类进行分析，尽量将load里的代码延后调用。</li>
<li>上面统计数据显示展示feed的导航控制器页面(NewsListViewController)比较耗时，对于viewDidLoad以及viewWillAppear方法中尽量去尝试少做，晚做，不做。</li>
</ul>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/interview/crash.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/interview/crash.html" class="post-title-link" itemprop="url">Crash Report</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-07-15 11:26:39" itemprop="dateCreated datePublished" datetime="2017-07-15T11:26:39+08:00">2017-07-15</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/" itemprop="url" rel="index"><span itemprop="name">面试总结</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/Crash-Report/" itemprop="url" rel="index"><span itemprop="name">Crash Report</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p><span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIuYXBwbGUuY29tL2xpYnJhcnkvYXJjaGl2ZS90ZWNobm90ZXMvdG4yMTUxL19pbmRleC5odG1s">官方文档,Understanding and Analyzing Application Crash Reports<i class="fa fa-external-link-alt"></i></span></p>
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>当app发生crash时，系统会生成crash report并存储在设备上.</p>
<ul>
<li><code>Crash Report</code>,  尤其是堆栈信息，在被符号化之前是不可读的。所谓符号化就是把内存地址用可读的函数名和行数来替换。 注意使用 .crash文件</li>
<li><code>Low Memory Report</code> 它没有堆栈信息</li>
</ul>
<img src="/blogwebpage/images/iOS/crash_guocheng.jpeg" class="" title="crash_guocheng">


<h1 id="符号化"><a href="#符号化" class="headerlink" title="符号化"></a>符号化</h1><p>编译代码的时候，通过build setting里的<code>Debug Information Format(DEBUG_INFORMATION_FORMAT)</code><br>临时介绍一下：</p>
<ul>
<li>关于DWARF与dSYM<ul>
<li>DWARF与dSYM的关系是，DWARF是文件格式，而dSYM往往指一个单独的文件。在Xcode中如果不做特殊制定，debug information是被保存在executable文件中，可以使用dsymutil从executable中提取dSYM文件。</li>
</ul>
</li>
<li>dsymutil<ul>
<li>dsymutil is a tool to manipulate archived DWARF debug symbol files. 使用dsymutil可以对dSYM文件进行如下操作：从exe_path中提取成dSYM文件、将executable或者object文件中的symbol table dump出来、更新dSYM文件以让dSYM文件包含最新的accelerator tables and other DWARF optimizations。</li>
</ul>
</li>
<li>Debug Info Format<ul>
<li>在Xcode中可以选择DWARF和DWARF with dSYM file，推荐的设置是Debug用DWARF；Release使用DWARF with dSYM file。</li>
</ul>
</li>
<li>使用dSYM file<ul>
<li>如果我们有若干的build，有若干dSYM文件，而名字又有点乱，想知道哪个dSYM跟哪个build匹配，从而可以使用它们呢？办法就是查看UUID。</li>
</ul>
</li>
</ul>
<ol>
<li>一般来说，debug模式构建的app会把Debug符号表存储在编译好的binary信息中，而release模式构建的app会把debug符号表存储在dSYM文件中以节省体积。</li>
</ol>
<blockquote>
<p>同一次构建，app+dSYM+UUID是一套的。如果这几个文件不属于同一次构建，即便是相同的源代码，互相之间在符号化这个事情上也无法互相工作。</p>
</blockquote>
<ol start="2">
<li>你为了分发app而选择Archive（存档）时，Xcode会把app的二进制信息和.dYSM文件存储在你的home文件夹下的某个地方。你可以在Xcode的Organizer里面通过”Archived”选项找到所有你存档过的app。</li>
</ol>
<blockquote>
<p>注意：想要解析来自于测试、app review或者客户的crash report，你需要保留分发出去的那些构建过的archive文件。</p>
</blockquote>
<ol start="3">
<li><p>如果你是通过App Store分发app或者是Test Flight分发的beta版本的app，你将在上传archive到ITC（iTunes Connect）时看见一个“是否将dSYM一起上传”的选项。在上传对话框中，请勾选”在app中包含app符号表”。上传你的dYSM文件对于从TestFlight用户和客户以及愿意分享诊断信息的客户那边接收crash report是很有必要的。</p>
<blockquote>
<p>注意：接收自App Review的crash report是不会被符号化的，及时你再上传你的app到ITC时勾选了包含dSYM文件。任何来自于App Review的crash report都需要在Xcode里做符号化。</p>
</blockquote>
</li>
<li><p>当你的app 发生crash时，一个没有被符号化的crash report会被创建并存储在设备上。</p>
</li>
<li><p>用户可以<span class="exturl" data-url="aHR0cHM6Ly9kZXZlbG9wZXIuYXBwbGUuY29tL2xpYnJhcnkvYXJjaGl2ZS9xYS9xYTE3NDcvX2luZGV4Lmh0bWw=">通过调试已部署的iOS APP<i class="fa fa-external-link-alt"></i></span>里提到的方法来直接从他们的设备里获得crash report。如果你通过AdHoc或者企业证书分发app，这是你唯一能从用户获取crash report的方法。</p>
</li>
<li><p>从设备上直接获取的crash report是没有被符号化的，你需要通过Xcode来符号化。Xcode会结合dSYM文件和你app的二进制信息把堆栈里的每一个地址对应到源代码中。处理后的结果就是一个符号化过的crash report。</p>
</li>
<li><p>如果用户愿意和Apple共享诊断信息，或者用户通过TestFlight下载了你的beta版本app，那crash report会被上传到App Store。</p>
</li>
<li><p>App Store在符号化crash report后会把内部所有的crash reports做汇总并分组，这种聚合（相似crash report）的方法叫做crash聚类。</p>
</li>
<li><p>这些符号化后的crash report可以在你的Xcode的Crash Organizer中进行查看。</p>
</li>
</ol>
<h1 id="Bitcode"><a href="#Bitcode" class="headerlink" title="Bitcode"></a>Bitcode</h1><p>Bitcode（位编码）是一个编译好的项目的中间表现形式。当你在允许bitcode的前提下Archive一个app时，编译器会在二进制中包含bitcode而不是机器码。一旦binary信息被上传到App Store中，bitcode会被再次编译成机器码。也许App Store会在将来二次编译bitcode</p>
<p>虽然当你Archive你的app时会创建dSYM文件，但它们只能用在bitcode binary信息中，并不能用于符号化crash report。 App Store允许你从Xcode或者ITC网站中下载这些随着bitcode编译而产生的dSYM文件。 为了解析从App Review或者给你发送crash report的用户的crash  report，你必须要下载这些dSYM文件，这样才能符号化crash report。 如果是从crash reporting service那里接收crash  report，符号化会自动完成。</p>
<blockquote>
<p>意思就是说，选择了Bitcode，你得从AppStore下载dSYM, 才是真正和binary信息匹配的，而不是你的中间码(提交的原始文件)</p>
</blockquote>
<h1 id="把”隐藏的”符号名还原成原始名"><a href="#把”隐藏的”符号名还原成原始名" class="headerlink" title="把”隐藏的”符号名还原成原始名"></a>把”隐藏的”符号名还原成原始名</h1><p>你把一个带有bitcode的app上传到App Store时，你也许在提交对话框中并没有勾选“上传你的app的符号表信息以便从Apple那边接收符号化过的 report”的选项。 当你选择不发送符号表信息给Apple时，Xcode会在你发送app到ITC之前用晦涩难懂的符号例如”_hidden#109”等来替换你的app里的dSYM文件。Xcode会创建一个原始符号和”隐藏”符号的对照表，并且将其存储在Archive的app文件中的一个bcsymbolmap文件里。每一个dSYM文件都会有一个对应的bcsymbolmap文件。</p>
<img src="/blogwebpage/images/iOS/bitcodeapp.png" class="" title="bitcodeapp">

<p>在符号化crash report之前，你需要把那些从ITC中下载下来的dSYM文件中的晦涩信息给解析一下。 如果你使用Xcode中的下载dSYM按钮，这步解析会自动完成。但是，如果你通过ITC网站来下载dSYM的话，你需要打开Terminal并且手动输入下面的命令来做解析（把example的path信息和sSYM信息替换一下）</p>
<blockquote>
<p>xcrun dsymutil -symbol-map ~/Library/Developer/Xcode/Archives/2017-11-23/MyGreatApp\ 11-23-17,\ 12.00\ PM.xcarchive/BCSymbolMaps ~/Downloads/dSYMs/3B15C133-88AA-35B0-B8BA-84AF76826CE0.dSYM</p>
</blockquote>
<p>针对每一个dSYMs文件夹下的dSYM文件都运行一次这条命令。</p>
<h1 id="用Xcode符号化iOS的Crash-report"><a href="#用Xcode符号化iOS的Crash-report" class="headerlink" title="用Xcode符号化iOS的Crash report"></a>用Xcode符号化iOS的Crash report</h1><p>一般来说，Xcode会自动尝试符号化它所有的Crash report。所以你只需要把crash report加到Xcode Organizer就可以了。<br><code>Note</code>：Xcode只认.crash后缀的crash report。如果你收到的crash report没有后缀名或者后缀是txt，在执行下列步骤之前先把它改成.crash。</p>
<ul>
<li>把iOS设备连接到你的Mac</li>
<li>从Window菜单栏选择Devices</li>
<li>在Devices左侧，选择一个设备</li>
<li>点击右边在“Device Information“ 下面的 ”View Device Logs” 按钮</li>
<li>把你的Crash report拖拽到左侧panel中</li>
<li>Xcode会自动符号化Crash report并且显示结果</li>
</ul>
<p>为了符号化一个Crash report，Xcode需要去定位如下信息：</p>
<ul>
<li>崩溃的app的binary信息以及dSYM文件</li>
<li>所有app关联的自定义framework的binary信息以及dSYM文件。如果是从app构建出来的framework，它们的dYSM会随着app的dSYM文件一起拷贝到archive中。如果是第三方的framework，你需要去找作者要dYSM文件。</li>
<li>发生crash时app所依赖的OS的符号表信息。这些符号表包含了特定OS版本（例如iOS9.3.3）上的framework所需调试信息。 OS 符号表的架构具有独特性——一个64位的iOS设备不会包含armv7的符号表。Xcode将要自动拷贝你连接到的特定版本的Mac的符号表。</li>
</ul>
<p>在上述任何一处，如果没有Xcode，你将无法符号化一个crash report，或者只能部分符号化一个crash report。</p>
<h1 id="用atos符号化Crash-report"><a href="#用atos符号化Crash-report" class="headerlink" title="用atos符号化Crash report"></a>用atos符号化Crash report</h1><p><code>atos</code>命令可以把地址里的数字替换成等价的符号。如果调试符号信息是完备的，则atos的输出信息将会包含文件名和对应的资源行数。atos命令可以被用来单独符号化那些未符号化或者部分符号化过的crash report（中的堆栈信息里的地址）。<br>想要使用atos符号化crash report可以按如下方式操作：</p>
<ol>
<li>找到你想要符号化的那一行，记下第二列的binary信息名，以及第三列的地址。</li>
<li>从crash report底部的binary信息名列表中找到那个名字，记下来架构名和加载的地址。</li>
</ol>
<p><code>atos -arch &lt;Binary Architecture&gt; -o &lt;Path to dSYM file&gt;/Contents/Resources/DWARF/&lt;binary image name&gt; -l &lt;load address&gt; &lt;address to symbolicate&gt;</code></p>
<p>使用atos命令的样例，以及结果输出</p>
<blockquote>
<p>$ atos -arch arm64 -o TheElements.app.dSYM/Contents/Resources/DWARF/TheElements -l 0x1000e4000 0x00000001000effdc</p>
</blockquote>
<blockquote>
<p>-[AtomicElementViewController myTransitionDidStop:finished:context:]</p>
</blockquote>
<h1 id="利用符号化排查问题"><a href="#利用符号化排查问题" class="headerlink" title="利用符号化排查问题"></a>利用符号化排查问题</h1><p>xcrun dwarfdump –uuid <Path to dSYM file><br>注意：你必须保存你最开始上传到App Store的发生crash的app的归档文件。dSYM文件和app二进制文件是一一对应，且每次构建都不相同。即便通过相同的源码和配置，再执行一次构建，生成的dSYM文件也无法和之前的crash report做符号化匹配。<br>如果你不在存有这个归档文件，你应该重新提交一次有归档的新版本，以确保再发生crash的时候你可以符号化crash report。</p>
<h1 id="关于异常信息分析，可以查看我写的另外一片怎么抓取崩溃信息那篇文章"><a href="#关于异常信息分析，可以查看我写的另外一片怎么抓取崩溃信息那篇文章" class="headerlink" title="关于异常信息分析，可以查看我写的另外一片怎么抓取崩溃信息那篇文章"></a>关于异常信息分析，可以查看我写的另外一片怎么抓取崩溃信息那篇文章</h1><p><a href="./iOS/crashreport/crashanalysis.html">去看</a></p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/interview/gcd-barrier.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/interview/gcd-barrier.html" class="post-title-link" itemprop="url">GCD_BARRIER</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-07-15 11:26:39" itemprop="dateCreated datePublished" datetime="2017-07-15T11:26:39+08:00">2017-07-15</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/" itemprop="url" rel="index"><span itemprop="name">面试总结</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/dispatch-barrier-async/" itemprop="url" rel="index"><span itemprop="name">dispatch_barrier_async</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="先注意！！！"><a href="#先注意！！！" class="headerlink" title="先注意！！！"></a>先注意！！！</h1><p>dispatch_barrier_async要在<code>自定义</code>的<code>并发队列</code>里！！！！<br>全局和串形达不到我们要的效果。<br>苹果文档中指出，如果使用的是全局队列或者创建的不是并发队列，则dispatch_barrier_async实际上就相当于dispatch_async。</p>
<p>另外dispatch_barrier_sync会阻塞当前线程，无意义。</p>
<h1 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h1><p>需求： 有n个任务， 需要开启多条线程去执行。 有一个特殊任务m， 需要在n1，n2任务执行完后， 再执行n3， n4任务。</p>
<h1 id="没了，还能有啥，怎么写吗？"><a href="#没了，还能有啥，怎么写吗？" class="headerlink" title="没了，还能有啥，怎么写吗？"></a>没了，还能有啥，怎么写吗？</h1>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/interview/iosappsigning.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/interview/iosappsigning.html" class="post-title-link" itemprop="url">iOS App 签名机制</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-07-15 11:26:39" itemprop="dateCreated datePublished" datetime="2017-07-15T11:26:39+08:00">2017-07-15</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/" itemprop="url" rel="index"><span itemprop="name">面试总结</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/iOS-App-%E7%AD%BE%E5%90%8D%E6%9C%BA%E5%88%B6/" itemprop="url" rel="index"><span itemprop="name">iOS App 签名机制</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>iOS App 签名机制</p>
<h1 id="先说一下名词"><a href="#先说一下名词" class="headerlink" title="先说一下名词"></a>先说一下名词</h1><h2 id="Certificates"><a href="#Certificates" class="headerlink" title="Certificates"></a>Certificates</h2><p>证书是用来给应用程序签名的，只有经过签名的应用程序才能保证他的来源是可信任的，并且代码是完整的， 未经修改的。<br>众所周知，我们申请一个Certificate之前，需要先申请一个Certificate Signing Request (CSR) 文件，而这个过程中实际上是生成了一对公钥和私钥，保存在你Mac的Keychain中。代码签名正是使用这种基于非对称秘钥的加密方式，用私钥进行签名，用公钥进行验证。如下图所示，在你Mac的keychain的login中存储着相关的公钥和私钥，而证书中包含了公钥。你只能用私钥来进行签名，所以如果没有了私钥，就意味着你不能进行签名了，所以就无法使用这个证书了，此时你只能revoke之前的证书再申请一个。因此在申请完证书时，最好导出并保存好你的私钥。当你想与其他人或其他设备共享证书时，把私钥传给它就可以了。私钥保存在你的Mac中，而苹果生成的Certificate中包含了公钥。当你用自己的私钥对代码签名后，苹果就可以用证书中的公钥来进行验证，确保是你对代码进行了签名，而不是别人冒充你，同时也确保代码的完整性等。</p>
<img src="/blogwebpage/images/iOS/cer.jpg" class="">
<p>证书主要分为两类：Development和Production，Development证书用来开发和调试应用程序，Production主要用来分发应用程序（根据证书种类有不同作用），下面是证书的分类信息：（括号内为证书有效期）</p>
<p>（注：不同类型的开发者账户所能创建的证书种类不同）</p>
<ul>
<li><p>Development</p>
<ul>
<li>App Development (1年)：用来开发和真机调试应用程序。</li>
<li>Push Development (1年)：用来调试Apple Push Notification</li>
</ul>
</li>
<li><p>Production</p>
<ul>
<li>In-House and Ad Hoc (3年)：用来发布In-House和AdHoc的应用程序。</li>
<li>App Store ：用来发布提交App Store的应用程序。</li>
<li>MDM CSR</li>
<li>Push Production (1年)：用来在发布版本中使用Apple Push Notification。</li>
<li>Pass Type ID Certificate</li>
<li>Website Push ID Certificate</li>
</ul>
</li>
</ul>
<h2 id="App-ID"><a href="#App-ID" class="headerlink" title="App ID"></a>App ID</h2><p>App ID用于标识一个或者一组App，App ID应该是和Xcode中的Bundle ID是一致的或者匹配的。App ID主要有以下两种：</p>
<p>Explicit App ID：唯一的App ID，这种App ID用于唯一标识一个应用程序，例如com.ABC.demo1，标识Bundle ID为com.ABC.demo1的程序。<br>Wildcard App ID：通配符App ID，用于标识一组应用程序。例如*可以表示所有应用程序，而com.ABC.*可以表示以com.ABC开头的所有应用程序。</p>
<img src="/blogwebpage/images/iOS/appidservice.jpg" class="" title="appidservice">

<p>如果你的App使用上述的任何一种service，就要按照要求去配置。</p>
<h2 id="Device"><a href="#Device" class="headerlink" title="Device"></a>Device</h2><p>Device最简单了，就是iOS设备。Devices中包含了该账户中所有可用于开发和测试的设备。 每台设备使用UDID来唯一标识。</p>
<p>每个账户中的设备数量限制是100个。Disable 一台设备也不会增加名额，只能在membership year 开始的时候才能通过删除设备来增加名额。</p>
<h2 id="Provisioning-Profile"><a href="#Provisioning-Profile" class="headerlink" title="Provisioning Profile"></a>Provisioning Profile</h2><p>一个Provisioning Profile文件包含了上述的所有内容：证书、App ID、设备。而且这个Provisioning Profile文件会在打包时嵌入.ipa的包里。</p>
<img src="/blogwebpage/images/iOS/ProvisioningProfile.jpg" class="" title="ProvisioningProfile">

<h1 id="言归正传说说签名原理"><a href="#言归正传说说签名原理" class="headerlink" title="言归正传说说签名原理"></a>言归正传说说签名原理</h1><p><span class="exturl" data-url="aHR0cDovL2Jsb2cuY25iYW5nLm5ldC90ZWNoLzMzODYv">参考大神博客<i class="fa fa-external-link-alt"></i></span><br>直接呈现大神总结的：</p>
<img src="/blogwebpage/images/iOS/sign3.png" class="">

<h2 id="再列一遍整个流程："><a href="#再列一遍整个流程：" class="headerlink" title="再列一遍整个流程："></a>再列一遍整个流程：</h2><ol>
<li>在你的 Mac 开发机器生成一对公私钥，这里称为公钥L，私钥L。L:Local</li>
<li>苹果自己有固定的一对公私钥，跟上面 AppStore 例子一样，私钥在苹果后台，公钥在每个 iOS 设备上。这里称为公钥A，私钥A。A:Apple</li>
<li>把公钥 L 传到苹果后台，用苹果后台里的私钥 A 去签名公钥 L。得到一份数据包含了公钥 L 以及其签名，把这份数据称为证书。</li>
<li>在苹果后台申请 AppID，配置好设备 ID 列表和 APP 可使用的权限，再加上第③步的证书，组成的数据用私钥 A 签名，把数据和签名一起组成一个 Provisioning Profile 文件，下载到本地 Mac 开发机。</li>
<li>在开发时，编译完一个 APP 后，用本地的私钥 L 对这个 APP 进行签名，同时把第④步得到的 Provisioning Profile 文件打包进 APP 里，文件名为 embedded.mobileprovision，把 APP 安装到手机上。</li>
<li>在安装时，iOS 系统取得证书，通过系统内置的公钥 A，去验证 embedded.mobileprovision 的数字签名是否正确，里面的证书签名也会再验一遍。</li>
<li>确保了 embedded.mobileprovision 里的数据都是苹果授权以后，就可以取出里面的数据，做各种验证，包括用公钥 L 验证APP签名，验证设备 ID 是否在 ID 列表上，AppID 是否对应得上，权限开关是否跟 APP 里的 Entitlements 对应等。</li>
</ol>
<p>开发者证书从签名到认证最终苹果采用的流程大致是这样，还有一些细节像证书有效期/证书类型等就不细说了。</p>
<h2 id="概念和操作"><a href="#概念和操作" class="headerlink" title="概念和操作"></a>概念和操作</h2><p>上面的步骤对应到我们平常具体的操作和概念是这样的：</p>
<ul>
<li>第 1 步对应的是 keychain 里的 “从证书颁发机构请求证书”，这里就本地生成了一对公私钥，保存的 CertificateSigningRequest 就是公钥，私钥保存在本地电脑里。</li>
<li>第 2 步苹果处理，不用管。</li>
<li>第 3 步对应把 CertificateSigningRequest 传到苹果后台生成证书，并下载到本地。这时本地有两个证书，一个是第 1 步生成的，一个是这里下载回来的，keychain 会把这两个证书关联起来，因为他们公私钥是对应的，在XCode选择下载回来的证书时，实际上会找到 keychain 里对应的私钥去签名。这里私钥只有生成它的这台 Mac 有，如果别的 Mac 也要编译签名这个 App 怎么办？答案是把私钥导出给其他 Mac 用，在 keychain 里导出私钥，就会存成 .p12 文件，其他 Mac 打开后就导入了这个私钥。</li>
<li>第 4 步都是在苹果网站上操作，配置 AppID / 权限 / 设备等，最后下载 Provisioning Profile 文件。</li>
<li>第 5 步 XCode 会通过第 3 步下载回来的证书（存着公钥），在本地找到对应的私钥（第一步生成的），用本地私钥去签名 App，并把 Provisioning Profile 文件命名为 embedded.mobileprovision 一起打包进去。这里对 App 的签名数据保存分两部分，Mach-O 可执行文件会把签名直接写入这个文件里，其他资源文件则会保存在 _CodeSignature 目录下。</li>
<li>第 6 – 7 步的打包和验证都是 Xcode 和 iOS 系统自动做的事。</li>
</ul>
<h2 id="这里再总结一下这些概念："><a href="#这里再总结一下这些概念：" class="headerlink" title="这里再总结一下这些概念："></a>这里再总结一下这些概念：</h2><ul>
<li>证书：内容是公钥或私钥，由其他机构对其签名组成的数据包。</li>
<li>Entitlements：包含了 App 权限开关列表。</li>
<li>CertificateSigningRequest：本地公钥。</li>
<li>p12：本地私钥，可以导入到其他电脑。</li>
<li>Provisioning Profile：包含了 证书 / Entitlements 等数据，并由苹果后台私钥签名的数据包。</li>
</ul>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/interview/pod.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/interview/pod.html" class="post-title-link" itemprop="url">Cocoapods原理</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-07-15 11:26:39" itemprop="dateCreated datePublished" datetime="2017-07-15T11:26:39+08:00">2017-07-15</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/" itemprop="url" rel="index"><span itemprop="name">面试总结</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/Cocoapods%E5%8E%9F%E7%90%86/" itemprop="url" rel="index"><span itemprop="name">Cocoapods原理</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="组成"><a href="#组成" class="headerlink" title="组成"></a>组成</h1><p>CocoaPods是用 Ruby 写的，并由若干个 Ruby 包 (gems) 构成的。在解析整合过程中，最重要的几个 gems 分别是： CocoaPods/CocoaPods, CocoaPods/Core, 和 CocoaPods/Xcodeproj (是的，CocoaPods 是一个依赖管理工具 – 利用依赖管理进行构建的！)。</p>
<h2 id="CocoaPods-CocoaPod"><a href="#CocoaPods-CocoaPod" class="headerlink" title="CocoaPods/CocoaPod"></a>CocoaPods/CocoaPod</h2><p>这是是一个面向用户的组件，每当执行一个 pod 命令时，这个组件都将被激活。该组件包括了所有使用 CocoaPods 涉及到的功能，并且还能通过调用所有其它的 gems 来执行任务。</p>
<h2 id="CocoaPods-Core"><a href="#CocoaPods-Core" class="headerlink" title="CocoaPods/Core"></a>CocoaPods/Core</h2><p>Core 组件提供支持与 CocoaPods 相关文件的处理，文件主要是 Podfile 和 podspecs。</p>
<h2 id="Podfile"><a href="#Podfile" class="headerlink" title="Podfile"></a>Podfile</h2><p>Podfile 是一个文件，用于定义项目所需要使用的第三方库。该文件支持高度定制，你可以根据个人喜好对其做出定制。更多相关信息，请查阅 Podfile 指南。</p>
<h2 id="Podspec"><a href="#Podspec" class="headerlink" title="Podspec"></a>Podspec</h2><p>.podspec 也是一个文件，该文件描述了一个库是怎样被添加到工程中的。它支持的功能有：列出源文件、framework、编译选项和某个库所需要的依赖等。</p>
<h2 id="CocoaPods-Xcodeproj"><a href="#CocoaPods-Xcodeproj" class="headerlink" title="CocoaPods/Xcodeproj"></a>CocoaPods/Xcodeproj</h2><p>这个 gem 组件负责所有工程文件的整合。它能够对创建并修改 .xcodeproj 和 .xcworkspace 文件。它也可以作为单独的一个 gem 包使用。如果你想要写一个脚本来方便的修改工程文件，那么可以使用这个 gem。</p>
<h1 id="加载源文件"><a href="#加载源文件" class="headerlink" title="加载源文件"></a>加载源文件</h1><p>CocoaPods 执行的下一步是加载源码。每个 .podspec 文件都包含一个源代码的索引，这些索引一般包裹一个 git 地址和 git tag。它们以 commit SHAs 的方式存储在 ~/Library/Caches/CocoaPods 中。这个路径中文件的创建是由 Core gem 负责的。</p>
<p>CocoaPods 将依照 Podfile、.podspec 和缓存文件的信息将源文件下载到 Pods 目录中。</p>
<h1 id="生成-Pods-xcodeproj"><a href="#生成-Pods-xcodeproj" class="headerlink" title="生成 Pods.xcodeproj"></a>生成 Pods.xcodeproj</h1><p>每次 pod install 执行，如果检测到改动时，CocoaPods 会利用 Xcodeproj gem 组件对 Pods.xcodeproj 进行更新。如果该文件不存在，则用默认配置生成。否则，会将已有的配置项加载至内存中。</p>
<h1 id="安装第三方库"><a href="#安装第三方库" class="headerlink" title="安装第三方库"></a>安装第三方库</h1><p>当 CocoaPods 往工程中添加一个第三方库时，不仅仅是添加代码这么简单，还会添加很多内容。由于每个第三方库有不同的 target，因此对于每个库，都会有几个文件需要添加，每个 target 都需要：</p>
<ul>
<li>一个包含编译选项的 .xcconfig 文件</li>
<li>一个同时包含编译设置和 CocoaPods 默认配置的私有 .xcconfig 文件</li>
<li>一个编译所必须的 prefix.pch 文件</li>
<li>另一个编译必须的文件 dummy.m</li>
</ul>
<p>一旦每个 pod 的 target 完成了上面的内容，整个 Pods target 就会被创建。这增加了相同文件的同时，还增加了另外几个文件。如果源码中包含有资源 bundle，将这个 bundle 添加至程序 target 的指令将被添加到 Pods-Resources.sh 文件中。还有一个名为 Pods-environment.h 的文件，文件中包含了一些宏，这些宏可以用来检查某个组件是否来自 pod。最后，将生成两个认可文件，一个是 plist，另一个是 markdown，这两个文件用于给最终用户查阅相关许可信息。</p>
<h1 id="写入至磁盘"><a href="#写入至磁盘" class="headerlink" title="写入至磁盘"></a>写入至磁盘</h1><p>直到现在，许多工作都是在内存中进行的。为了让这些成果能被重复利用，我们需要将所有的结果保存到一个文件中。所以 Pods.xcodeproj 文件被写入磁盘，另外两个非常重要的文件：Podfile.lock 和 Manifest.lock 都将被写入磁盘。</p>
<ul>
<li><p>Podfile.lock<br>这是 CocoaPods 创建的最重要的文件之一。它记录了需要被安装的 pod 的每个已安装的版本。如果你想知道已安装的 pod 是哪个版本，可以查看这个文件。推荐将 Podfile.lock 文件加入到版本控制中，这有助于整个团队的一致性。</p>
</li>
<li><p>Manifest.lock<br>这是每次运行 pod install 命令时创建的 Podfile.lock 文件的副本。如果你遇见过这样的错误 沙盒文件与 Podfile.lock 文件不同步 (The sandbox is not in sync with the Podfile.lock)，这是因为 Manifest.lock 文件和 Podfile.lock 文件不一致所引起。由于 Pods 所在的目录并不总在版本控制之下，这样可以保证开发者运行 app 之前都能更新他们的 pods，否则 app 可能会 crash，或者在一些不太明显的地方编译失败。</p>
</li>
<li><p>xcproj<br>如果你已经依照我们的建议在系统上安装了 xcproj，它会对 Pods.xcodeproj 文件执行一下 touch 以将其转换成为旧的 ASCII plist 格式的文件。为什么要这么做呢？虽然在很久以前就不被其它软件支持了，但是 Xcode 仍然依赖于这种格式。如果没有 xcproj，你的 Pods.xcodeproj 文件将会以 XML 格式的 plist 文件存储，当你用 Xcode 打开它时，它会被改写，并造成大量的文件改动。</p>
</li>
</ul>
<h1 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h1><p>依赖库有Pod工程管理，最终编译为.a文件或者framework文件，为主项目使用</p>
<p>libPods.a<br>Pods-resources.sh<br>Pods.xcconfig</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/interview/nsurlsession-connection.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/interview/nsurlsession-connection.html" class="post-title-link" itemprop="url">NSURLSession与NSURLConnection区别</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-07-15 11:26:39" itemprop="dateCreated datePublished" datetime="2017-07-15T11:26:39+08:00">2017-07-15</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/" itemprop="url" rel="index"><span itemprop="name">面试总结</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/NSURLSession%E4%B8%8ENSURLConnection%E5%8C%BA%E5%88%AB/" itemprop="url" rel="index"><span itemprop="name">NSURLSession与NSURLConnection区别</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="NSURLSession与NSURLConnection区别"><a href="#NSURLSession与NSURLConnection区别" class="headerlink" title="NSURLSession与NSURLConnection区别"></a>NSURLSession与NSURLConnection区别</h2><p>2013 年的 WWDC 大会上，iOS 7.0 推出了 NSURLSession，对 Foundation URL 加载系统进行了彻底的重构，提供了更丰富的 API 来处理网络请求，如：支持 http2.0 协议、直接把数据下载到磁盘、同一 session 发送多个请求、下载是多线程异步处理和提供全局的 session 并可以统一配置等等，提高了 NSURLSession 的易用性、灵活性，更加地适合移动开发的需求。</p>
<p>在iOS9以后，NSURLConnection过期废弃.</p>
<p>NSURLSession 有三种网络操作方案：</p>
<ul>
<li>普通网络请求   <code>NSURLSessionDataTask</code></li>
<li>上传          <code>NSURLSessionUploadTask</code></li>
<li>下载          <code>NSURLSessionDownloadTask</code></li>
</ul>
<p>创建的 task 都是挂起状态，需要 resume 才能启动.</p>
<h2 id="普通网络请求-和-上传网络请求"><a href="#普通网络请求-和-上传网络请求" class="headerlink" title="普通网络请求 和 上传网络请求"></a>普通网络请求 和 上传网络请求</h2><ul>
<li>普通:  当服务器返回的数据较小时，NSURLSession 与 NSURLConnection 执行普通任务的操作步骤没有区别。</li>
<li>上传:  执行上传任务时，NSURLSession 与 NSURLConnection 一样需要设置 POST 请求的请求体进行上传。</li>
</ul>
<h2 id="下载任务方式"><a href="#下载任务方式" class="headerlink" title="下载任务方式"></a>下载任务方式</h2><ul>
<li>NSURLConnection下载文件时，先是将整个文件下载到内存，然后再写入到沙盒，如果文件比较大，就会出现内存暴涨的情况。</li>
<li>而使用 NSURLSessionDownloadTask 下载文件，会默认下载到沙盒中的 tmp 文件中，不会出现内存暴涨的情况，但是在下载完成后会把 tmp 中的临时文件删除，需要在初始化任务方法时，在 completionHandler 回调中增加保存文件的代码。</li>
</ul>
<h2 id="请求方法的控制"><a href="#请求方法的控制" class="headerlink" title="请求方法的控制"></a>请求方法的控制</h2><ul>
<li>NSURLConnection 实例化对象，实例化开始，默认请求就发送(同步发送)，不需要调用 start 方法。而 cancel 可以停止请求的发送，停止后不能继续访问，需要创建新的请求。</li>
<li>NSURLSession 有三个控制方法，取消(cancel)、暂停(suspend)、继续(resume)，暂停以后可以通过继续恢复当前的请求任务。</li>
</ul>
<h2 id="断点续传的方式"><a href="#断点续传的方式" class="headerlink" title="断点续传的方式"></a>断点续传的方式</h2><ul>
<li><p>NSURLConnection 进行断点下载，通过设置访问请求的 HTTPHeaderField 的 Range 属性，开启运行循环，NSURLConnection 的代理方法作为运行循环的事件源，接收到下载数据时代理方法就会持续调用，并使用 NSOutputStream 管道流进行数据保存。</p>
</li>
<li><p>NSURLSession 进行断点下载，当暂停下载任务后，如果 downloadTask（下载任务）为非空，调用 cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler 这个方法，这个方法接收一个参数，完成处理代码块，这个代码块有一个 NSData 参数 resumeData，如果 resumeData 非空，我们就保存这个对象到视图控制器的 resumeData 属性中，在点击再次下载时，通过调用[ [self.session downloadTaskWithResumeData:self.resumeData] resume]方法进行继续下载操作。</p>
</li>
</ul>
<p>经过以上比较可以发现，使用 NSURLSession 进行断点下载更加便捷。</p>
<h2 id="配置信息"><a href="#配置信息" class="headerlink" title="配置信息"></a>配置信息</h2><blockquote>
<p><code>NSURLSessionConfiguration</code> 类的参数可以设置配置信息，其决定了 <code>cookie</code>，<code>安全</code>和<code>高速缓存策略</code>，<code>最大主机连接数</code>，<code>资源管理</code>，<code>网络超时</code>等配置。NSURLConnection 不能进行这个配置，相比较与 NSURLConnection 依赖与一个<code>全局的配置</code>对象，缺乏灵活性而言，NSURLSession 有很大的改进了。</p>
</blockquote>
<p>有三个方法来创建NSURLSessionConfiguration:</p>
<ul>
<li><p>defaultSessionConfiguration 使用全局的cache，cookie,使用硬盘来缓存数据。</p>
</li>
<li><p>ephemeralSessionConfiguration 临时session配置，与默认配置相比，这个配置不会将缓存、cookie等存在本地，只会存在内存里，所以当程序退出时，所有的数据都会消失</p>
</li>
<li><p>backgroundSessionConfiguration 后台session配置，与默认配置类似，不同的是会在后台开启另一个线程来处理网络数据。</p>
</li>
</ul>
<p>一旦创建了NSURLSessionConfiguration就可以给它设置各种属性</p>
<p><code>看NSURLSessionConfiguration的头文件:</code></p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">NSURLSessionConfiguration</span> : <span class="title">NSObject</span> &lt;<span class="title">NSCopying</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 三种创建方式 */</span></span><br><span class="line"></span><br><span class="line">+ (<span class="built_in">NSURLSessionConfiguration</span> *)defaultSessionConfiguration;</span><br><span class="line">+ (<span class="built_in">NSURLSessionConfiguration</span> *)ephemeralSessionConfiguration;</span><br><span class="line">+ (<span class="built_in">NSURLSessionConfiguration</span> *)backgroundSessionConfigurationWithIdentifier:(<span class="built_in">NSString</span> *)identifier <span class="built_in">NS_AVAILABLE</span>(<span class="number">10</span>_10, <span class="number">8</span>_0);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 当使用上述第三种方式创建后台sessionConfiguration时可以读到初始化时传入的唯一标识，其他创建方式都为空 */</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nullable</span>, <span class="keyword">readonly</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *identifier;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">缓存策略，默认值是NSURLRequestUseProtocolCachePolicy</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">NSURLRequestCachePolicy</span> requestCachePolicy;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 给request指定每次接收数据超时间隔，如果下一次接受新数据用时超过该值，则发送一个请求超时给该request。默认为60s */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">NSTimeInterval</span> timeoutIntervalForRequest;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 给指定resource设定一个超时时间，resource需要在时间到达之前完成。默认是7天。 */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">NSTimeInterval</span> timeoutIntervalForResource;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 指定网络传输类型。精切指出传输类型，可以让系统快速响应，提高传输质量，延长电池寿命等。</span></span><br><span class="line"><span class="comment">typedef NS_ENUM(NSUInteger, NSURLRequestNetworkServiceType)</span></span><br><span class="line"><span class="comment">&#123;</span></span><br><span class="line"><span class="comment">    NSURLNetworkServiceTypeDefault = 0, // 普通网络传输，默认使用这个</span></span><br><span class="line"><span class="comment">    NSURLNetworkServiceTypeVoIP = 1,    // 网络语音通信传输，只能在VoIP使用</span></span><br><span class="line"><span class="comment">    NSURLNetworkServiceTypeVideo = 2,   // 影像传输</span></span><br><span class="line"><span class="comment">    NSURLNetworkServiceTypeBackground = 3, // 网络后台传输，优先级不高时可使用。对用户不需要的网络操作可使用</span></span><br><span class="line"><span class="comment">    NSURLNetworkServiceTypeVoice = 4       // 语音传输</span></span><br><span class="line"><span class="comment">&#125;;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">NSURLRequestNetworkServiceType</span> networkServiceType;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 是否使用蜂窝网络，默认是yes. */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">BOOL</span> allowsCellularAccess;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 是否由系统根据性能自动裁量后台任务。默认值是NO。同sessionSendsLaunchEvent一样，只对后台configuration有效。 */</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">getter</span>=isDiscretionary) <span class="built_in">BOOL</span> discretionary <span class="built_in">NS_AVAILABLE</span>(<span class="number">10</span>_10, <span class="number">7</span>_0);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">如果要为app的插件提供session，需要给这个值赋值</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nullable</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *sharedContainerIdentifier <span class="built_in">NS_AVAILABLE</span>(<span class="number">10</span>_10, <span class="number">8</span>_0);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment"> 表示当后台传输结束时，是否启动app.这个属性只对 后台sessionConfiguration 生效，其他configuration类型会自动忽略该值。默认值是YES。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">BOOL</span> sessionSendsLaunchEvents <span class="built_in">NS_AVAILABLE</span>(NA, <span class="number">7</span>_0);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">指定了会话连接中的代理服务器。同样地，大多数面向消费者的应用程序都不需要代理，所以基本上不需要配置这个属性,默认为NULL</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nullable</span>, <span class="keyword">copy</span>) <span class="built_in">NSDictionary</span> *connectionProxyDictionary;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 确定是否支持SSLProtocol版本的会话</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> SSLProtocol TLSMinimumSupportedProtocol;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">确定是否支持SSLProtocol版本的会话</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">@property</span> SSLProtocol TLSMaximumSupportedProtocol;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">它可以被用于开启HTTP管道，这可以显着降低请求的加载时间，但是由于没有被服务器广泛支持，默认是禁用的</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">BOOL</span> HTTPShouldUsePipelining;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">默认为yes,是否提供来自shareCookieStorge的cookie，如果想要自己提供cookie，可以使用HTTPAdditionalHeaders来提供。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">BOOL</span> HTTPShouldSetCookies;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Policy for accepting cookies.  This overrides the policy otherwise specified by the cookie storage. */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">NSHTTPCookieAcceptPolicy</span> HTTPCookieAcceptPolicy;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">指定了一组默认的可以设置出站请求的数据头。这对于跨会话共享信息，如内容类型，语言，用户代理，身份认证，是很有用的。</span></span><br><span class="line"><span class="comment">例如：</span></span><br><span class="line"><span class="comment">    @&#123;@&quot;Accept&quot;: @&quot;application/json&quot;,</span></span><br><span class="line"><span class="comment">     @&quot;Accept-Language&quot;: @&quot;en&quot;,</span></span><br><span class="line"><span class="comment">     @&quot;Authorization&quot;: authString,</span></span><br><span class="line"><span class="comment">     @&quot;User-Agent&quot;: userAgentString</span></span><br><span class="line"><span class="comment">   &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nullable</span>, <span class="keyword">copy</span>) <span class="built_in">NSDictionary</span> *HTTPAdditionalHeaders;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">同时连接一个host的最大数。iOS默认是4.APP是作为一个整体来看的</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">NSInteger</span> HTTPMaximumConnectionsPerHost;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">存储cookie，清除存储，直接set为nil即可。</span></span><br><span class="line"><span class="comment">对于默认和后台的session，使用sharedHTTPCookieStorage。</span></span><br><span class="line"><span class="comment">对于短暂的session，cookie仅仅储存到内存，session失效时会自动清除。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nullable</span>, <span class="keyword">retain</span>) <span class="built_in">NSHTTPCookieStorage</span> *HTTPCookieStorage;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">证书存储，如果不使用，可set为nil.</span></span><br><span class="line"><span class="comment">默认和后台session，默认使用的sharedCredentialStorage.</span></span><br><span class="line"><span class="comment">短暂的session使用一个私有存储在内存中。session失效会自动清除。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nullable</span>, <span class="keyword">retain</span>) <span class="built_in">NSURLCredentialStorage</span> *URLCredentialStorage;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">缓存NSURLRequest的response。</span></span><br><span class="line"><span class="comment">默认的configuration，默认值的是sharedURLCache。</span></span><br><span class="line"><span class="comment">后台的configuration，默认值是nil</span></span><br><span class="line"><span class="comment">短暂的configuration，默认一个私有的cache于内存，session失效，cache自动清除。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nullable</span>, <span class="keyword">retain</span>) <span class="built_in">NSURLCache</span> *URLCache;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Enable extended background idle mode for any tcp sockets created.    Enabling this mode asks the system to keep the socket open</span></span><br><span class="line"><span class="comment"> *  and delay reclaiming it when the process moves to the background (see https://developer.apple.com/library/ios/technotes/tn2277/_index.html) </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> <span class="built_in">BOOL</span> shouldUseExtendedBackgroundIdleMode <span class="built_in">NS_AVAILABLE</span>(<span class="number">10</span>_11, <span class="number">9</span>_0);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">处理NSURLRequest的NSURLProtocol的子类。</span></span><br><span class="line"><span class="comment">重要:对后台Session失效。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">@property</span> (<span class="keyword">nullable</span>, <span class="keyword">copy</span>) <span class="built_in">NSArray</span>&lt;Class&gt; *protocolClasses;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@end</span></span><br></pre></td></tr></table></figure>

<h2 id="URLSessionTask"><a href="#URLSessionTask" class="headerlink" title="URLSessionTask"></a>URLSessionTask</h2><p>NSURLSessionTask是一个抽象类，其下有4个实体子类可以直接使用：NSURLSessionDataTask、NSURLSessionUploadTask、NSURLSessionDownloadTask、NSURLSessionStreamTask。这四个子类封装了现代程序四个最基本的网络任务：获取数据，比如JSON或者XML，上传文件和下载文件还有数据流的获取。</p>
<img src="/blogwebpage/images/iOS/NSURLSessionTask.png" class="">

<p>NSURLSession比NSURLConnection最方便的地方就是任务可以暂停，继续。在网络请求中，真正去执行下载或者上传任务的就是URLSessionTask，我们来看一下它常用的方法：<br><code>- (void)resume;</code> 当使用NSURLSession创建一个NSURLSessionTask任务时，要手动调用此方法，任务才会开启，而NSURLConnection默认开启。<br><code>- (void)suspend;</code> 暂停任务方法，手动调用会暂停当前任务，再次开启此任务时，会从紧接上次任务开始，会面会说到如何暂停任务再开启任务。<br><code>- (void)cancel;</code> 取消任务。</p>
<p>NSURLSessionTask还有个属性，@property (readonly) NSURLSessionTaskState state; 此属性标识当前任务的状态，枚举类型</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="built_in">NS_ENUM</span>(<span class="built_in">NSInteger</span>, <span class="built_in">NSURLSessionTaskState</span>) &#123;</span><br><span class="line">    <span class="built_in">NSURLSessionTaskStateRunning</span> = <span class="number">0</span>,                     <span class="comment">/* 正在执行 */</span></span><br><span class="line">    <span class="built_in">NSURLSessionTaskStateSuspended</span> = <span class="number">1</span>,                   <span class="comment">/* 暂停状态 */</span></span><br><span class="line">    <span class="built_in">NSURLSessionTaskStateCanceling</span> = <span class="number">2</span>,                   <span class="comment">/* 取消状态*/</span></span><br><span class="line">    <span class="built_in">NSURLSessionTaskStateCompleted</span> = <span class="number">3</span>,                   <span class="comment">/* 任务完成状态 */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面说到的四个类，都直接或间接继承NSURLSessionTask，所有NSURLSessionTask的方法或者属性这四个类都有，那么，简单说一下这四个类都是干什么的。</p>
<h3 id="NSURLSessionDataTask"><a href="#NSURLSessionDataTask" class="headerlink" title="NSURLSessionDataTask"></a>NSURLSessionDataTask</h3><p>NSURLSessionDataTask是开发中使用频率最高的，我们平常使用的GET和POST请求都是通过它来实现的，如果请求的数据简单并且不需要对获取的数据进行复杂操作，我们使用 Block 解析返回的数据即可。</p>
<p>另外我们也可以设置session的代理来实时的监听数据，我们可以使用NSURLSession的<code>+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;</code>和<code>+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id &lt;NSURLSessionDelegate&gt;)delegate delegateQueue:(nullable NSOperationQueue *)queue;</code>这两个方法来设置代理，具体的协议为<code>NSURLSessionDelegate</code>，它有四个直接或间接子协议<code>NSURLSessionTaskDelegate</code>，<code>NSURLSessionDownloadDelegate</code>和 <code>NSURLSessionStreamDelegate</code>、<code>NSURLSessionDataDelegate</code></p>
<h3 id="NSURLSessionDownloadTask"><a href="#NSURLSessionDownloadTask" class="headerlink" title="NSURLSessionDownloadTask"></a>NSURLSessionDownloadTask</h3><p>NSURLSessionDownloadTask在下载文件的时候，是将数据一点点地写入本地的临时文件。所以在 completionHandler 这个 block 里，我们需要把文件从一个临时地址移动到一个永久的地址保存起来：</p>
<h3 id="断点续传"><a href="#断点续传" class="headerlink" title="断点续传"></a>断点续传</h3><p>说一下开发中经常用到的断点续传。在开发中，我们经常由于某种原因，在下载或上传的时候往往不能一次性下载或上传完，有可能下载或上传了一半就终止了，这时候当条件满足继续下载或上传时，我们不希望从头开始，这时候就可以使用断点续传。它的大概思路是：</p>
<ul>
<li>某种限制，续传暂停</li>
<li>将暂停后数据（当前数据）保存起来–_resumeData = resumeData;</li>
<li>条件允许续传时，使用resumeData创建新的NSURLSessionTask</li>
</ul>
<h3 id="NSURLSessionUploadTask"><a href="#NSURLSessionUploadTask" class="headerlink" title="NSURLSessionUploadTask"></a>NSURLSessionUploadTask</h3><p>在 NSURLSession 中，文件上传主要使用两种方式：</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="built_in">NSURLSessionUploadTask</span> *)uploadTaskWithRequest:(<span class="built_in">NSURLRequest</span> *)request fromFile:(<span class="built_in">NSURL</span> *)fileURL;</span><br><span class="line">- (<span class="built_in">NSURLSessionUploadTask</span> *)uploadTaskWithRequest:(<span class="built_in">NSURLRequest</span> *)request fromData:(<span class="built_in">NSData</span> *)bodyData;</span><br></pre></td></tr></table></figure>

<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ol>
<li>NSURLSession 是iOS7后出来的替代 NSURLConnection 的API;</li>
<li>NSURLSession 通过绑定一个 delegate 在一个网络会话的生命周期里调用某些事件；</li>
<li>NSURLSession 对象是线程安全的；</li>
<li>NSURLSession 默认使用系统提供的delegate，并适当的使用现有的代码使用；</li>
<li>NSURLSession 通过创建 NSURLSessionTask 代表资源被加载的任务，类似 NSURLConnection对象，但相比有更多可控制和定制的 delegate 模型；</li>
<li>NSURLSessionTask 对象创建后是处于挂起状态，只有发-resume消息才会执行；</li>
<li>NSURLSessionTask 的子类 在加载 Data 和 文件下载的使用上有所不同；</li>
<li>NSURLSessionDataTask 接收到资源时会回调 RLSession:dataTask:didReceiveData: 代理method；</li>
<li>NSURLSessionUploadTask 与 NSURLSessionDataTask 的构造方法不同，他需求显示的引用file或data object；</li>
<li>NSURLSessionDownloadTask 会直接把response data写入到临时文件，网络会话结束后，delegate 会发送一个 URLSession:downloadTask:didFinishDownloadingToURL: 消息来处理下载文件，如果中途取消则会生成一个Blob Data以便下次恢复下载；</li>
<li>从iOS9开始，使用NSURLSessionStream可以通过一个给定的主机和端口直接建立TCP/IP连接；</li>
</ol>
<h3 id="NSURLSession的优点"><a href="#NSURLSession的优点" class="headerlink" title="NSURLSession的优点"></a>NSURLSession的优点</h3><ul>
<li><code>后台上传和下载：</code> 只需在创建NSURLSession的时候配置一个选项，就能得到后台网络的所有好处;</li>
<li><code>能够暂停和恢复网络操作：</code> 使用NSURLSession API能够暂停，停止，恢复所有的网络任务，再也完全不需要子类化NSOperation;</li>
<li><code>可配置的容器：</code> 对于NSURLSession里面的requests来说，每个NSURLSession都是可配置的容器。举个例来说，假如你需要设置HTTP header选项，你只用做一次，session里面的每个request就会有同样的配置。</li>
<li><code>提高认证处理：</code>认证是在一个指定的连接基础上完成的。在使用NSURLConnection时，如果发出一个访问，会返回一个任意的request。此时，你就不能确切的知道哪个request收到了访问。而在NSURLSession中，就能用代理处理认证。</li>
<li><code>丰富的代理模式：</code> 在处理认证的时候，NSURLConnection有一些基于异步的block方法，但是它的代理方法就不能处理认证，不管请求是成功或是失败。在NSURLSession中，可以混合使用代理和block方法处理认证。</li>
</ul>
<h1 id="多看下-AFNetworking-的实现"><a href="#多看下-AFNetworking-的实现" class="headerlink" title="多看下 AFNetworking 的实现"></a>多看下 AFNetworking 的实现</h1>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://wangkejie.gitee.io/blogwebpage/iOS/interview/ipaanalysis.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blogwebpage/images/avatar.gif">
      <meta itemprop="name" content="Jack Wang">
      <meta itemprop="description" content="书山有路勤为径，学海无涯苦作舟！">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="王科杰">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/blogwebpage/iOS/interview/ipaanalysis.html" class="post-title-link" itemprop="url">ipa包分析</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-07-15 11:26:39" itemprop="dateCreated datePublished" datetime="2017-07-15T11:26:39+08:00">2017-07-15</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/" itemprop="url" rel="index"><span itemprop="name">iOS</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/" itemprop="url" rel="index"><span itemprop="name">面试总结</span></a>
                </span>
                  ，
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blogwebpage/categories/iOS/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/ipa%E5%8C%85%E5%88%86%E6%9E%90/" itemprop="url" rel="index"><span itemprop="name">ipa包分析</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>ipa解包后结构:</p>
<ul>
<li>可执行文件<ul>
<li>Mach-O 64-bit executable arm64</li>
<li>Mach-O executable arm_v7</li>
</ul>
</li>
<li>.car，资源打包文件 .nib、.bundle、Localizable.strings</li>
<li>_CodeSignature签名文件<ul>
<li>文件hash列表：存放每个文件的hash值； </li>
<li>XML结构</li>
</ul>
</li>
<li>.mobileprovision </li>
</ul>
<p>可执行文件Mach-O通常有三部分组成</p>
<ul>
<li>头部 (Header): Mach-O文件的架构 比如Mac的 PPC, PPC64, IA-32, x86-64，ios的arm系列。</li>
<li>加载命令(Load commands): 在虚拟内存中指定文件的逻辑结构和布局。</li>
<li>原始段数据（Raw segment data）:可以拥有多个段（segment），每个段可以拥有零个或多个区域（section）。每一个段（segment）都拥有一段虚拟地址映射到进程的地址空间。</li>
</ul>
<img src="/blogwebpage/images/iOS/macho_construction.png" class="" title="Mach-O_Construction">

<ol>
<li><p>XCode开启编译选项Write Link Map File<br>XCode -&gt; Project -&gt; Build Settings -&gt; 搜map -&gt; 把Write Link Map File选项设为YES，并指定好linkMap的存储位置<br>特别提醒：打包发布前记得还原为NO</p>
</li>
<li><p>编译后，到编译目录里找到该txt文件，文件名和路径就是上述的Path to Link Map File位于</p>
<figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">~<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/XXX-XXXXXXXXXXXX/</span>Build<span class="regexp">/Intermediates/</span>XXX.build<span class="regexp">/Debug-iphoneos/</span>XXX.build/</span><br><span class="line"></span><br><span class="line"><span class="regexp">//</span>example</span><br><span class="line"><span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build/<span class="number">4</span>dBookCity-LinkMap-normal-arm64.txt</span><br></pre></td></tr></table></figure>
<p>这个LinkMap里展示了整个可执行文件的全貌，列出了编译后的每一个.o目标文件的信息（包括静态链接库.a里的），以及每一个目标文件的代码段，数据段存储详情。</p>
</li>
</ol>
<h2 id="LinkMap结构"><a href="#LinkMap结构" class="headerlink" title="LinkMap结构"></a>LinkMap结构</h2><ol>
<li><p>首先列出来的是目标文件列表(中括号内为文件编号)：</p>
<figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"># Path: <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Products/</span>Debug-iphoneos<span class="regexp">/4dBookCity.app/</span><span class="number">4</span>dBookCity</span><br><span class="line"># Arch: arm64</span><br><span class="line"># Object files:</span><br><span class="line">[  <span class="number">0</span>] linker synthesized</span><br><span class="line">[  <span class="number">1</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build<span class="regexp">/Objects-normal/</span>arm64/Bulk_Arrays_12.o</span><br><span class="line">[  <span class="number">2</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build<span class="regexp">/Objects-normal/</span>arm64/MXRSnapLearnInviteView.o</span><br><span class="line">[  <span class="number">3</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build<span class="regexp">/Objects-normal/</span>arm64/MXRPKHomeCellViewModel.o</span><br><span class="line">[  <span class="number">4</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build<span class="regexp">/Objects-normal/</span>arm64/Bulk_Arrays_5.o</span><br><span class="line">[  <span class="number">5</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build<span class="regexp">/Objects-normal/</span>arm64/MXRBookStoreItemScrollTemplateCell.o</span><br><span class="line">[  <span class="number">6</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build<span class="regexp">/Objects-normal/</span>arm64/MXRAutoReadViewController.o</span><br><span class="line">[  <span class="number">7</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build<span class="regexp">/Objects-normal/</span>arm64/MXRQAExerciseQestionTitleView.o</span><br><span class="line">[  <span class="number">8</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build<span class="regexp">/Objects-normal/</span>arm64/MXRMyTaskController.o</span><br><span class="line">[  <span class="number">9</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build<span class="regexp">/Objects-normal/</span>arm64/UnityView.o</span><br><span class="line">[ <span class="number">10</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Intermediates.noindex/</span>huashida_home.build<span class="regexp">/Debug-iphoneos/</span><span class="number">4</span>dBookCity.build<span class="regexp">/Objects-normal/</span>arm64/main.o</span><br><span class="line">...</span><br><span class="line">[<span class="number">5229</span>] <span class="regexp">/Users/m</span>xr<span class="regexp">/Library/</span>Developer<span class="regexp">/Xcode/</span>DerivedData<span class="regexp">/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/</span>Build<span class="regexp">/Products/</span>Debug-iphoneos/libPods-<span class="number">4</span>dBookCity.a(Pods-<span class="number">4</span>dBookCity-dummy.o)</span><br><span class="line">[<span class="number">5230</span>] <span class="regexp">/Applications/</span>Xcode.app<span class="regexp">/Contents/</span>Developer<span class="regexp">/Toolchains/</span>XcodeDefault.xctoolchain<span class="regexp">/usr/</span>lib<span class="regexp">/arc/</span>libarclite_iphoneos.a(arclite.o)</span><br><span class="line">[<span class="number">5231</span>] <span class="regexp">/Applications/</span>Xcode.app<span class="regexp">/Contents/</span>Developer<span class="regexp">/Platforms/i</span>PhoneOS.platform<span class="regexp">/Developer/</span>SDKs<span class="regexp">/iPhoneOS11.2.sdk/u</span>sr<span class="regexp">/lib/</span>libobjc.tbd</span><br><span class="line">[<span class="number">5232</span>] <span class="regexp">/Applications/</span>Xcode.app<span class="regexp">/Contents/</span>Developer<span class="regexp">/Toolchains/</span>XcodeDefault.xctoolchain<span class="regexp">/usr/</span>lib<span class="regexp">/clang/</span><span class="number">9.0</span>.<span class="number">0</span><span class="regexp">/lib/</span>darwin/libclang_rt.ios.a(os_version_check.c.o)</span><br><span class="line">[<span class="number">5233</span>] <span class="regexp">/Applications/</span>Xcode.app<span class="regexp">/Contents/</span>Developer<span class="regexp">/Platforms/i</span>PhoneOS.platform<span class="regexp">/Developer/</span>SDKs<span class="regexp">/iPhoneOS11.2.sdk/</span>System<span class="regexp">/Library/</span>Frameworks<span class="comment">//AudioToolbox.framework/AudioToolbox.tbd</span></span><br><span class="line">[<span class="number">5234</span>] <span class="regexp">/Applications/</span>Xcode.app<span class="regexp">/Contents/</span>Developer<span class="regexp">/Platforms/i</span>PhoneOS.platform<span class="regexp">/Developer/</span>SDKs<span class="regexp">/iPhoneOS11.2.sdk/</span>System<span class="regexp">/Library/</span>Frameworks<span class="comment">//CoreVideo.framework/CoreVideo.tbd</span></span><br></pre></td></tr></table></figure></li>
<li><p>接着是一个段表，描述各个段在最后编译成的可执行文件中的偏移位置及大小，包括了代码段（__TEXT，保存程序代码段编译后的机器码）和数据段（__DATA，保存变量值）</p>
<figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Sections:</span></span><br><span class="line"><span class="comment"># Address   Size        Segment Section</span></span><br><span class="line"><span class="attribute">0x100005B00</span> <span class="number">0</span>x<span class="number">0304</span>A<span class="number">29</span>C  __TEXT  __text</span><br><span class="line"><span class="attribute">0x10304FD9C</span> <span class="number">0</span>x<span class="number">00004</span>BC<span class="number">0</span>  __TEXT  __stubs</span><br><span class="line"><span class="attribute">0x10305495C</span> <span class="number">0</span>x<span class="number">000044</span>E<span class="number">8</span>  __TEXT  __stub_helper</span><br><span class="line"><span class="attribute">0x103058E50</span> <span class="number">0</span>x<span class="number">0021563</span>C  __TEXT  __cstring</span><br><span class="line"><span class="attribute">0x10326E48C</span> <span class="number">0</span>x<span class="number">000</span>AD<span class="number">400</span>  __TEXT  __objc_methname</span><br><span class="line"><span class="attribute">0x10331B88C</span> <span class="number">0</span>x<span class="number">0000</span>E<span class="number">6</span>BA  __TEXT  __objc_classname</span><br><span class="line"><span class="attribute">0x103329F46</span> <span class="number">0</span>x<span class="number">000166</span>E<span class="number">3</span>  __TEXT  __objc_methtype</span><br><span class="line"><span class="attribute">0x103340640</span> <span class="number">0</span>x<span class="number">002</span>A<span class="number">0</span>B<span class="number">60</span>  __TEXT  __const</span><br><span class="line"><span class="attribute">0x1035E11A0</span> <span class="number">0</span>x<span class="number">001346</span>D<span class="number">4</span>  __TEXT  __gcc_except_tab</span><br><span class="line"><span class="attribute">0x103715874</span> <span class="number">0</span>x<span class="number">00008</span>C<span class="number">78</span>  __TEXT  __ustring</span><br><span class="line"><span class="attribute">0x10371E4EC</span> <span class="number">0</span>x<span class="number">0004</span>D<span class="number">80</span>C  __TEXT  __unwind_info</span><br><span class="line"><span class="attribute">0x10376BCF8</span> <span class="number">0</span>x<span class="number">00000300</span>  __TEXT  __eh_frame</span><br><span class="line"><span class="attribute">0x10376C000</span> <span class="number">0</span>x<span class="number">000015</span>D<span class="number">8</span>  __DATA  __got</span><br><span class="line"><span class="attribute">0x10376D5D8</span> <span class="number">0</span>x<span class="number">00003280</span>  __DATA  __la_symbol_ptr</span><br><span class="line"><span class="attribute">0x103770858</span> <span class="number">0</span>x<span class="number">00001838</span>  __DATA  __mod_init_func</span><br><span class="line"><span class="attribute">0x103772090</span> <span class="number">0</span>x<span class="number">000</span>FF<span class="number">7</span>F<span class="number">8</span>  __DATA  __const</span><br><span class="line"><span class="attribute">0x103871888</span> <span class="number">0</span>x<span class="number">0006</span>F<span class="number">9</span>C<span class="number">0</span>  __DATA  __cfstring</span><br><span class="line"><span class="attribute">0x1038E1248</span> <span class="number">0</span>x<span class="number">00004778</span>  __DATA  __objc_classlist</span><br><span class="line"><span class="attribute">0x1038E59C0</span> <span class="number">0</span>x<span class="number">00000290</span>  __DATA  __objc_nlclslist</span><br><span class="line"><span class="attribute">0x1038E5C50</span> <span class="number">0</span>x<span class="number">00000708</span>  __DATA  __objc_catlist</span><br><span class="line"><span class="attribute">0x1038E6358</span> <span class="number">0</span>x<span class="number">00000038</span>  __DATA  __objc_nlcatlist</span><br><span class="line"><span class="attribute">0x1038E6390</span> <span class="number">0</span>x<span class="number">00000910</span>  __DATA  __objc_protolist</span><br><span class="line"><span class="attribute">0x1038E6CA0</span> <span class="number">0</span>x<span class="number">00000008</span>  __DATA  __objc_imageinfo</span><br><span class="line"><span class="attribute">0x1038E6CA8</span> <span class="number">0</span>x<span class="number">00206</span>C<span class="number">58</span>  __DATA  __objc_const</span><br><span class="line"><span class="attribute">0x103AED900</span> <span class="number">0</span>x<span class="number">00027</span>F<span class="number">28</span>  __DATA  __objc_selrefs</span><br><span class="line"><span class="attribute">0x103B15828</span> <span class="number">0</span>x<span class="number">000000</span>C<span class="number">0</span>  __DATA  __objc_protorefs</span><br><span class="line"><span class="attribute">0x103B158E8</span> <span class="number">0</span>x<span class="number">000041</span>B<span class="number">8</span>  __DATA  __objc_classrefs</span><br><span class="line"><span class="attribute">0x103B19AA0</span> <span class="number">0</span>x<span class="number">000030</span>C<span class="number">0</span>  __DATA  __objc_superrefs</span><br><span class="line"><span class="attribute">0x103B1CB60</span> <span class="number">0</span>x<span class="number">0000</span>BB<span class="number">54</span>  __DATA  __objc_ivar</span><br><span class="line"><span class="attribute">0x103B286B8</span> <span class="number">0</span>x<span class="number">0002</span>CB<span class="number">00</span>  __DATA  __objc_data</span><br><span class="line"><span class="attribute">0x103B551C0</span> <span class="number">0</span>x<span class="number">01</span>D<span class="number">52748</span>  __DATA  __data</span><br><span class="line"><span class="attribute">0x1058A7920</span> <span class="number">0</span>x<span class="number">00714878</span>  __DATA  __bss</span><br><span class="line"><span class="attribute">0x105FBD000</span> <span class="number">0</span>x<span class="number">0012</span>B<span class="number">978</span>  __DATA  __common</span><br></pre></td></tr></table></figure>
<p>首列是数据在文件的偏移位置，第二列是这一段占用大小，第三列是段类型，代码段和数据段，第四列是段名称。<br>每一行的数据都紧跟在上一行后面，如第二行__stubs的地址0x10304FD9C就是第一行__text的地址0x100005B00加上大小0x0304A29C，整个可执行文件大致数据分布就是这样。<br>这里可以清楚看到各种类型的数据在最终可执行文件里占的比例，例如__text表示编译后的程序执行语句，__data表示已初始化的全局变量和局部静态变量，__bss表示未初始化的全局变量和局部静态变量，__cstring表示代码里的字符串常量，等等。</p>
</li>
<li><p>接着就是按上表顺序，列出具体的按每个文件列出每个对应字段的位置和占用空间</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Symbols:</span></span><br><span class="line"><span class="comment"># Address   Size        File  Name</span></span><br><span class="line"><span class="number">0x100005B00</span> <span class="number">0x000000EC</span>  [  <span class="number">2</span>] <span class="string">-[MXRSnapLearnInviteView</span> <span class="string">drawRect:]</span></span><br><span class="line"><span class="number">0x100005BEC</span> <span class="number">0x0000024C</span>  [  <span class="number">2</span>] <span class="string">-[MXRSnapLearnInviteView</span> <span class="string">generatorlogoImageQRCode]</span></span><br><span class="line"><span class="number">0x100005E38</span> <span class="number">0x0000005C</span>  [  <span class="number">2</span>] <span class="string">_CGRectMake</span></span><br><span class="line"><span class="number">0x100005E94</span> <span class="number">0x00000034</span>  [  <span class="number">2</span>] <span class="string">-[MXRSnapLearnInviteView</span> <span class="string">inviteCode]</span></span><br><span class="line"><span class="number">0x100005EC8</span> <span class="number">0x00000050</span>  [  <span class="number">2</span>] <span class="string">-[MXRSnapLearnInviteView</span> <span class="string">setInviteCode:]</span></span><br><span class="line"><span class="number">0x100005F18</span> <span class="number">0x0000003C</span>  [  <span class="number">2</span>] <span class="string">-[MXRSnapLearnInviteView</span> <span class="string">.cxx_destruct]</span></span><br><span class="line"><span class="number">0x100005F54</span> <span class="number">0x000001D8</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">initWithModel:]</span></span><br><span class="line"><span class="number">0x10000612C</span> <span class="number">0x0000016C</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">encodeWithCoder:]</span></span><br><span class="line"><span class="number">0x100006298</span> <span class="number">0x00000268</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">initWithCoder:]</span></span><br><span class="line"><span class="number">0x100006500</span> <span class="number">0x00000040</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">desc]</span></span><br><span class="line"><span class="number">0x100006540</span> <span class="number">0x00000044</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">setDesc:]</span></span><br><span class="line"><span class="number">0x100006584</span> <span class="number">0x00000040</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">name]</span></span><br><span class="line"><span class="number">0x1000065C4</span> <span class="number">0x00000044</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">setName:]</span></span><br><span class="line"><span class="number">0x100006608</span> <span class="number">0x00000040</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">pic]</span></span><br><span class="line"><span class="number">0x100006648</span> <span class="number">0x00000044</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">setPic:]</span></span><br><span class="line"><span class="number">0x10000668C</span> <span class="number">0x00000040</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">classifyId]</span></span><br><span class="line"><span class="number">0x1000066CC</span> <span class="number">0x00000044</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">setClassifyId:]</span></span><br><span class="line"><span class="number">0x100006710</span> <span class="number">0x000000B8</span>  [  <span class="number">3</span>] <span class="string">-[MXRPKHomeCellViewModel</span> <span class="string">.cxx_destruct]</span></span><br><span class="line"><span class="string">...</span></span><br><span class="line"><span class="number">0x1060C82D0</span> <span class="number">0x000000C0</span>  [<span class="number">3391</span>] <span class="string">_jerrenv</span></span><br><span class="line"><span class="number">0x1060C8390</span> <span class="number">0x000204E0</span>  [<span class="number">4793</span>] <span class="string">_GC_arrays</span></span><br><span class="line"><span class="number">0x1060E8870</span> <span class="number">0x00000100</span>  [<span class="number">4793</span>] <span class="string">_GC_bm_table</span></span><br><span class="line"><span class="number">0x1060E8970</span> <span class="number">0x00000008</span>  [<span class="number">4793</span>] <span class="string">_GC_noop_sink</span></span><br></pre></td></tr></table></figure>
<p>同样首列是数据在文件的偏移地址，第二列是占用大小，第三列是所属文件序号，对应上述Object files列表，最后是名字。</p>
</li>
<li><p>已废弃&amp;多余重复的字段</p>
<figure class="highlight angelscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"># Dead Stripped Symbols:</span><br><span class="line">#           Size        File  Name</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000001</span>  [  <span class="number">1</span>] literal <span class="built_in">string</span>: </span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000005</span>  [  <span class="number">3</span>] literal <span class="built_in">string</span>: desc</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000005</span>  [  <span class="number">3</span>] literal <span class="built_in">string</span>: name</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000004</span>  [  <span class="number">3</span>] literal <span class="built_in">string</span>: pic</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x0000000B</span>  [  <span class="number">3</span>] literal <span class="built_in">string</span>: classifyId</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x0000000E</span>  [  <span class="number">3</span>] literal <span class="built_in">string</span>: .cxx_destruct</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x0000000B</span>  [  <span class="number">3</span>] literal <span class="built_in">string</span>: <span class="symbol">v24@</span><span class="number">0</span>:<span class="symbol">8@</span><span class="number">16</span></span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">3</span>] literal <span class="built_in">string</span>: <span class="symbol">v16@</span><span class="number">0</span>:<span class="number">8</span></span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">3</span>] literal <span class="built_in">string</span>: @<span class="symbol">16@</span><span class="number">0</span>:<span class="number">8</span></span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000001</span>  [  <span class="number">4</span>] literal <span class="built_in">string</span>: </span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000007</span>  [  <span class="number">4</span>] literal <span class="built_in">string</span>: System</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x0000000C</span>  [  <span class="number">4</span>] literal <span class="built_in">string</span>: UnityEngine</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x0000000A</span>  [  <span class="number">4</span>] literal <span class="built_in">string</span>: System.IO</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] <span class="number">8</span>-byte-literal</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x0000000C</span>  [  <span class="number">5</span>] literal <span class="built_in">string</span>: PRIMARY KEY</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x0000000C</span>  [  <span class="number">5</span>] literal <span class="built_in">string</span>: FOREIGN KEY</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000001</span>  [  <span class="number">5</span>] literal <span class="built_in">string</span>: </span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000020</span>  [  <span class="number">5</span>] CFString</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000020</span>  [  <span class="number">5</span>] CFString</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Type_Text</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Type_Int</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Type_Double</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Type_Blob</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Attribute_NotNull</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Attribute_PrimaryKey</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Attribute_Default</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Attribute_Unique</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Attribute_Check</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Attribute_ForeignKey</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Convert_FloatType</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Convert_IntType</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Convert_BlobType</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Mapping_Inherit</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Mapping_Binding</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKSQL_Mapping_UserCalculate</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKDB_TypeKey</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKDB_TypeKey_Model</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKDB_TypeKey_JSON</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [  <span class="number">5</span>] _LKDB_TypeKey_Combo</span><br><span class="line">...</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000004</span>  [<span class="number">4311</span>] <span class="number">4</span>-byte-literal</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000004</span>  [<span class="number">4311</span>] <span class="number">4</span>-byte-literal</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000004</span>  [<span class="number">4311</span>] <span class="number">4</span>-byte-literal</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000004</span>  [<span class="number">4311</span>] <span class="number">4</span>-byte-literal</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [<span class="number">4311</span>] <span class="number">8</span>-byte-literal</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000008</span>  [<span class="number">4312</span>] <span class="number">8</span>-byte-literal</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000014</span>  [<span class="number">4320</span>] __ZN15PxcConvexMeshHLC2EP17PxConvexMeshData_</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000004</span>  [<span class="number">4320</span>] <span class="number">4</span>-byte-literal</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000004</span>  [<span class="number">4320</span>] <span class="number">4</span>-byte-literal</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000004</span>  [<span class="number">4319</span>] <span class="number">4</span>-byte-literal</span><br><span class="line">&lt;&lt;dead&gt;&gt;    <span class="number">0x00000076</span>  [<span class="number">4319</span>] literal <span class="built_in">string</span>: /Applications/buildAgent/work/<span class="number">3</span>d1e9e6e6eefaa7f/SDKs/compiler/iphone/../../../LowLevel/common/include/utils/PxcArray.h</span><br></pre></td></tr></table></figure>
<p>这个文件可以让你了解整个APP编译后的情况，也许从中可以发现一些异常，还可以用这个文件计算静态链接库在项目里占的大小，有时候我们在项目里链了很多第三方库，导致APP体积变大很多，我们想确切知道每个库占用了多大空间，可以给我们优化提供方向。LinkMap里有了每个目标文件每个方法每个数据的占用大小数据，所以只要写个脚本，就可以统计出每个.o最后的大小，属于一个.a静态链接库的.o加起来，就是这个库在APP里占用的空间大小。</p>
</li>
</ol>
<h2 id="关于Xcode的Other-Linker-Flags"><a href="#关于Xcode的Other-Linker-Flags" class="headerlink" title="关于Xcode的Other Linker Flags"></a>关于Xcode的Other Linker Flags</h2><ul>
<li>背景</li>
</ul>
<p>在ios开发过程中，有时候会用到第三方的静态库(.a文件)，然后导入后发现编译正常但运行时会出现selector not recognized的错误，从而导致app闪退。接着仔细阅读库文件的说明文档，你可能会在文档中发现诸如在Other Linker Flags中加入-ObjC或者-all_load这样的解决方法。<br>那么，Other Linker Flags到底是用来干什么的呢？还有-ObjC和-all_load到底发挥了什么作用呢？</p>
<ul>
<li>链接器</li>
</ul>
<p>首先，要说明一下Other Linker Flags到底是用来干嘛的。说白了，就是ld命令除了默认参数外的其他参数。ld命令实现的是链接器的工作，详细说明可以在终端man ld查看。<br>如果有人不清楚链接器是什么东西的话，我可以作个简单的说明。<br>一个程序从简单易读的代码到可执行文件往往要经历以下步骤：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">源代码 &gt; 预处理器 &gt; 编译器 &gt; 汇编器 &gt; 机器码 &gt; 链接器 &gt; 可执行文件</span><br></pre></td></tr></table></figure>
<p>源文件经过一系列处理以后，会生成对应的.obj文件，然后一个项目必然会有许多.obj文件，并且这些文件之间会有各种各样的联系，例如函数调用。链接器做的事就是把这些目标文件和所用的一些库链接在一起形成一个完整的可执行文件。</p>
<ul>
<li>为什么会闪退</li>
</ul>
<p>Objective-C的链接器并不会为每个方法建立符号表，而是仅仅为类建立了符号表。这样的话，如果静态库中定义了已存在的一个类的分类，链接器就会以为这个类已经存在，不会把分类和核心类的代码合起来。这样的话，在最后的可执行文件中，就会缺少分类里的代码，这样函数调用就失败了。</p>
<ul>
<li>解决方法</li>
</ul>
<p>解决方法在背景那块我就提到了，就是在Other Linker Flags里加上所需的参数，用到的参数一般有以下3个：</p>
<figure class="highlight autohotkey"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">`-ObjC`       : 链接器会把静态库中所有的类和分类都加载到最后的可执行文件中</span><br><span class="line">`-force_load` : 需要指定要进行全部加载的库文件的路径,避免引用多个第三方库时会出现类名重叠的冲突</span><br><span class="line">`-all_load`   : 让链接器把所有找到的目标文件都加载到可执行文件中，不建议使用</span><br><span class="line">`-dead_strip` : 删除多余的库符号，不建议使用</span><br></pre></td></tr></table></figure>
<p>在编译Objective-C源文件到目标文件时，编译器并不知道方法的对应实现，只能在运行时才知道，所以编译器只会为类生成链接符号，对类中的方法不会生成链接符号。由于Category方法并不对应一个新类，所以不会生成链接符号，链接器也不会将Category方法合并到原始的类中，最终导致链接器忽略了Category方法，不会将其链接到可执行文件中。</p>
<p>当静态库中只有分类而没有类的时候，-ObjC参数就会失效了。这时候，就需要使用-all_load或者-force_load了。<br>-all_load会让链接器把所有找到的目标文件都加载到可执行文件中，但是千万不要随便使用这个参数！假如你使用了不止一个静态库文件，然后又使用了这个参数，那么你很有可能会遇到ld: duplicate symbol错误，因为不同的库文件里面可能会有相同的目标文件，所以建议在遇到-ObjC失效的情况下使用-force_load参数。<br>-force_load所做的事情跟-all_load其实是一样的，但是-force_load需要指定要进行全部加载的库文件的路径，这样的话，你就只是完全加载了一个库文件，不影响其余库文件的按需加载。<br>在能拿到静态库源码情况下，建议对.a库重新打包，删除部分重复的symbol。<br>在拿不到静态库源码情况下 ,只能采用-force_load+库文件路径方法设置Other Linker Flags,逐个加静态库,最终完美解决两个静态库存在同名文件冲突,发现那个静态库无法调用,就采用以下语句添加进去。<br>-force_load EightPartyCall/standaloneclass/BaiduSocialShare/WX/libWeChatSDK<br>(-force_load后面为静态库文件路径,根据自己项目对应路径)</p>
<p>也可以拆分静态库</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$ cd /LibSDK </span><br><span class="line">$ ls</span><br><span class="line">libiot<span class="selector-class">.sdk</span>.a</span><br><span class="line">$ lipo -info libiot<span class="selector-class">.sdk</span>.a</span><br><span class="line">Architectures <span class="keyword">in</span> the fat file: libiot<span class="selector-class">.sdk</span><span class="selector-class">.a</span> are: armv7 arm64 </span><br><span class="line">$  lipo libiot<span class="selector-class">.sdk</span><span class="selector-class">.a</span> -thin armv7 -output tbv7.a</span><br><span class="line">$  lipo libiot<span class="selector-class">.sdk</span><span class="selector-class">.a</span> -thin arm64 -output tb64.a</span><br><span class="line">$ ls</span><br><span class="line">libiot<span class="selector-class">.sdk</span><span class="selector-class">.a</span>        tb64<span class="selector-class">.a</span>      tbv7.a</span><br><span class="line">$ ar -d tbv7<span class="selector-class">.a</span> AsyncSocket.o</span><br><span class="line">$ ar -d tb64<span class="selector-class">.a</span> AsyncSocket.o</span><br><span class="line">$ lipo -create tbv7<span class="selector-class">.a</span> tbv64<span class="selector-class">.a</span> -output libSun.a</span><br></pre></td></tr></table></figure>

<p>lipo源于mac系统要制作兼容powerpc平台和intel平台的程序。<br>lipo 是一个在 Mac OS X 中处理通用程序（Universal Binaries）的工具。现在发售或者提供下载的许多（几乎所有）程序都打上了“Universal”标志，意味着它们同时具有 PowerPC 和 Intel 芯片能够处理的代码。不过既然你可能不在意其中的一个，你就能够使用 lipo 来给你的程序“瘦身”。</p>
<h2 id="写了一堆乱七八糟的"><a href="#写了一堆乱七八糟的" class="headerlink" title="写了一堆乱七八糟的"></a>写了一堆乱七八糟的</h2><p>参考app包优化<br><span class="exturl" data-url="aHR0cHM6Ly93d3cuamlhbnNodS5jb20vcC9hMzE1MWRmZWJjOWM=">干货|今日头条iOS端安装包大小优化—思路与实践<i class="fa fa-external-link-alt"></i></span></p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  


  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/blogwebpage/page/3/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/blogwebpage/">1</a><span class="space">&hellip;</span><a class="page-number" href="/blogwebpage/page/3/">3</a><span class="page-number current">4</span><a class="page-number" href="/blogwebpage/page/5/">5</a><a class="page-number" href="/blogwebpage/page/6/">6</a><a class="extend next" rel="next" href="/blogwebpage/page/5/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
  </nav>



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Jack Wang"
      src="/blogwebpage/images/avatar.gif">
  <p class="site-author-name" itemprop="name">Jack Wang</p>
  <div class="site-description" itemprop="description">书山有路勤为径，学海无涯苦作舟！</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/blogwebpage/archives/">
        
          <span class="site-state-item-count">52</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/blogwebpage/categories/">
          
        <span class="site-state-item-count">56</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/blogwebpage/tags/">
          
        <span class="site-state-item-count">54</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL0phY2stVmluZw==" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;Jack-Ving"><i class="github fa-fw"></i>GitHub</span>
      </span>
      <span class="links-of-author-item">
        <span class="exturl" data-url="bWFpbHRvOjcyNzg4MTk0NUBxcS5jb20=" title="E-Mail → mailto:727881945@qq.com"><i class="envelope fa-fw"></i>E-Mail</span>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        
  <div class="beian"><span class="exturl" data-url="aHR0cHM6Ly9iZWlhbi5taWl0Lmdvdi5jbg==">京ICP备16036665号-2 </span>
  </div>

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Jack Wang</span>
</div>
  <div class="powered-by">由 <span class="exturl theme-link" data-url="aHR0cHM6Ly9oZXhvLmlv">Hexo</span> & <span class="exturl theme-link" data-url="aHR0cHM6Ly90aGVtZS1uZXh0Lm9yZw==">NexT.Gemini</span> 强力驱动
  </div>

        


  <div style="display: none;">
    <script src="//s95.cnzz.com/z_stat.php?id=1277804316&web_id=1277804316"></script>
  </div>






      </div>
    </footer>
  </div>

  
  <script src="/blogwebpage/lib/anime.min.js"></script>
  <script src="/blogwebpage/lib/velocity/velocity.min.js"></script>
  <script src="/blogwebpage/lib/velocity/velocity.ui.min.js"></script>

<script src="/blogwebpage/js/utils.js"></script>

<script src="/blogwebpage/js/motion.js"></script>


<script src="/blogwebpage/js/schemes/pisces.js"></script>


<script src="/blogwebpage/js/next-boot.js"></script>




  
  <script>
    (function(){
      var canonicalURL, curProtocol;
      //Get the <link> tag
      var x=document.getElementsByTagName("link");
		//Find the last canonical URL
		if(x.length > 0){
			for (i=0;i<x.length;i++){
				if(x[i].rel.toLowerCase() == 'canonical' && x[i].href){
					canonicalURL=x[i].href;
				}
			}
		}
    //Get protocol
	    if (!canonicalURL){
	    	curProtocol = window.location.protocol.split(':')[0];
	    }
	    else{
	    	curProtocol = canonicalURL.split(':')[0];
	    }
      //Get current URL if the canonical URL does not exist
	    if (!canonicalURL) canonicalURL = window.location.href;
	    //Assign script content. Replace current URL with the canonical URL
      !function(){var e=/([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,r=canonicalURL,t=document.referrer;if(!e.test(r)){var n=(String(curProtocol).toLowerCase() === 'https')?"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif":"//api.share.baidu.com/s.gif";t?(n+="?r="+encodeURIComponent(document.referrer),r&&(n+="&l="+r)):r&&(n+="?l="+r);var i=new Image;i.src=n}}(window);})();
  </script>




  
<script src="/blogwebpage/js/local-search.js"></script>













  

  
  <script src="//cdn.jsdelivr.net/npm/quicklink@1/dist/quicklink.umd.js"></script>
  <script>
      window.addEventListener('load', () => {
      quicklink({
        timeout : 3000,
        priority: true,
        ignores : [uri => uri.includes('#'),uri => uri === 'https://wangkejie.gitee.io/blogwebpage/page/4/',]
      });
      });
  </script>

</body>
</html>
